From 833779f6ee9c450a2a18ce06415730e1f329be1d Mon Sep 17 00:00:00 2001 From: Eric Seidel Date: Thu, 21 Mar 2024 03:57:24 -0700 Subject: [PATCH] feat: sketch out a trade_in behavior --- packages/cli/lib/behavior/trade_in.dart | 63 +++++++++++++++++++++++++ packages/cli/lib/net/queries.dart | 7 +++ packages/db/lib/db.dart | 9 ++++ packages/db/lib/src/shipyard_price.dart | 11 +++++ 4 files changed, 90 insertions(+) create mode 100644 packages/cli/lib/behavior/trade_in.dart diff --git a/packages/cli/lib/behavior/trade_in.dart b/packages/cli/lib/behavior/trade_in.dart new file mode 100644 index 00000000..bf4a902b --- /dev/null +++ b/packages/cli/lib/behavior/trade_in.dart @@ -0,0 +1,63 @@ +import 'package:cli/behavior/job.dart'; +import 'package:cli/caches.dart'; +import 'package:cli/central_command.dart'; +import 'package:cli/nav/exploring.dart'; +import 'package:cli/net/actions.dart'; +import 'package:cli/net/queries.dart'; +import 'package:cli/plan/ships.dart'; +import 'package:db/db.dart'; +import 'package:types/types.dart'; + +/// Upcycle the ship. +Future doTradeInJob( + BehaviorState state, + Api api, + Database db, + CentralCommand centralCommand, + Caches caches, + Ship ship, { + DateTime Function() getNow = defaultGetNow, +}) async { + // Will also dock the ship. + await visitLocalShipyard( + db, + api, + caches.waypoints, + caches.static, + caches.agent, + ship, + ); + // Get the purchase price of a new ship of this type. + final shipType = assertNotNull( + guessShipType(caches.static.shipyardShips, ship), + 'No ship type found.', + const Duration(minutes: 5), + ); + + final price = assertNotNull( + await db.shipyardPriceAt(ship.waypointSymbol, shipType), + 'No price found.', + const Duration(minutes: 5), + ); + + final scrapTransaction = assertNotNull( + await getScrapValue(api, ship.symbol), + 'No scrap value found', + const Duration(minutes: 5), + ); + final scrapValue = scrapTransaction.totalPrice; + jobAssert( + scrapValue > price.purchasePrice, + 'Scrap value is too low.', + const Duration(minutes: 5), + ); + // New ships are cheaper than the scrap value, trade in! + await purchaseShip(db, api, caches.agent, ship.waypointSymbol, shipType); + await scrapShipAndLog(api, db, caches.agent, ship); + return JobResult.complete(); +} + +/// Advance the trade in. +final advanceTradeIn = const MultiJob('Trade In', [ + doTradeInJob, +]).run; diff --git a/packages/cli/lib/net/queries.dart b/packages/cli/lib/net/queries.dart index a8af3fd1..3994a2e3 100644 --- a/packages/cli/lib/net/queries.dart +++ b/packages/cli/lib/net/queries.dart @@ -104,6 +104,13 @@ Future getMarket(Api api, WaypointSymbol waypointSymbol) async { return response!.data; } +/// Fetches Scrap value for a given Ship. +/// Ship must be docked at a shipyard. +Future getScrapValue(Api api, ShipSymbol symbol) async { + final response = await api.fleet.getScrapShip(symbol.symbol); + return response?.data.transaction; +} + /// Fetches Construction for a given Waypoint. Future getConstruction( Api api, diff --git a/packages/db/lib/db.dart b/packages/db/lib/db.dart index 469aff62..6df56cfd 100644 --- a/packages/db/lib/db.dart +++ b/packages/db/lib/db.dart @@ -481,6 +481,15 @@ class Database { return queryMany(allShipyardPricesQuery(), shipyardPriceFromColumnMap); } + /// Get the shipyard price for the given waypoint and ship type. + Future shipyardPriceAt( + WaypointSymbol waypointSymbol, + ShipType shipType, + ) async { + final query = shipyardPriceQuery(waypointSymbol, shipType); + return queryOne(query, shipyardPriceFromColumnMap); + } + /// Add a shipyard price to the database. Future upsertShipyardPrice(ShipyardPrice price) async { await execute(upsertShipyardPriceQuery(price)); diff --git a/packages/db/lib/src/shipyard_price.dart b/packages/db/lib/src/shipyard_price.dart index 7958eeca..cb576ffa 100644 --- a/packages/db/lib/src/shipyard_price.dart +++ b/packages/db/lib/src/shipyard_price.dart @@ -16,6 +16,17 @@ Query upsertShipyardPriceQuery(ShipyardPrice price) => Query( parameters: shipyardPriceToColumnMap(price), ); +/// Query to get the shipyard price for a given waypoint and ship type. +/// Returns null if no price is found. +Query shipyardPriceQuery(WaypointSymbol symbol, ShipType shipType) => Query( + 'SELECT * FROM shipyard_price_ ' + 'WHERE waypoint_symbol = @symbol AND ship_type = @ship_type', + parameters: { + 'symbol': symbol.toJson(), + 'ship_type': shipType.toJson(), + }, + ); + /// Query to get the timestamp of the most recent shipyard price for a waypoint. Query timestampOfMostRecentShipyardPriceQuery(WaypointSymbol symbol) => Query( 'SELECT MAX(timestamp) FROM shipyard_price_ '