[golang] implement trading and contracting
This commit is contained in:
parent
a692a38d28
commit
312ef2eb57
12 changed files with 422 additions and 39 deletions
|
@ -15,7 +15,7 @@ func (a *agent) autoContracting(ship *model.Ship) {
|
|||
return
|
||||
}
|
||||
for _, contract := range contracts {
|
||||
if contract.Fullfilled {
|
||||
if contract.Fulfilled {
|
||||
continue
|
||||
}
|
||||
now := time.Now()
|
||||
|
@ -27,11 +27,17 @@ func (a *agent) autoContracting(ship *model.Ship) {
|
|||
}
|
||||
}
|
||||
a.channel <- fmt.Errorf("negotiating new contracts is not implemented yet")
|
||||
// TODO
|
||||
//for {
|
||||
// negotiate
|
||||
// runContract
|
||||
//}
|
||||
for {
|
||||
contract, err := a.client.NegotiateContract(ship)
|
||||
if err != nil {
|
||||
a.channel <- fmt.Errorf("failed to negotiate contract: %w", err)
|
||||
return
|
||||
}
|
||||
if err := a.runContract(contract, ship); err != nil {
|
||||
a.channel <- fmt.Errorf("failed to run contract %s: %w", contract.Id, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *agent) runContract(contract *model.Contract, ship *model.Ship) error {
|
||||
|
@ -51,15 +57,33 @@ func (a *agent) runContract(contract *model.Contract, ship *model.Ship) error {
|
|||
}
|
||||
|
||||
func (a *agent) runProcurement(contract *model.Contract, ship *model.Ship) error {
|
||||
deliveryCargo := contract.Terms.Deliver[0].TradeSymbol
|
||||
deliveryWaypoint, err := a.client.GetWaypoint(contract.Terms.Deliver[0].DestinationSymbol, a.db)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get delivery waypoint: %w", err)
|
||||
if contract.Fulfilled {
|
||||
return nil
|
||||
}
|
||||
for !contract.Fullfilled {
|
||||
_ = deliveryCargo
|
||||
_ = deliveryWaypoint
|
||||
return fmt.Errorf("not implemented")
|
||||
deliver := contract.Terms.Deliver[0]
|
||||
// make sure we are not carrying useless stuff
|
||||
if err := a.sellEverythingExcept(ship, deliver.TradeSymbol); err != nil {
|
||||
return fmt.Errorf("failed to sell everything except %s for ship %s: %w", deliver.TradeSymbol, ship.Symbol, err)
|
||||
}
|
||||
return fmt.Errorf("not implemented")
|
||||
// procure the desired goods
|
||||
if ship.Cargo.Units < min(deliver.UnitsRequired-deliver.UnitsFulfilled, ship.Cargo.Capacity) {
|
||||
if err := a.buyTradeGood(ship, deliver.TradeSymbol); err != nil {
|
||||
return fmt.Errorf("failed to buy trade good %s with ship %s: %w", deliver.TradeSymbol, ship.Symbol, err)
|
||||
}
|
||||
}
|
||||
// deliver the goods
|
||||
if err := a.client.Navigate(ship, deliver.DestinationSymbol, a.db); err != nil {
|
||||
return fmt.Errorf("failed to navigate to %s: %w", deliver.DestinationSymbol, err)
|
||||
}
|
||||
if err := a.client.Deliver(contract, ship, a.db); err != nil {
|
||||
return fmt.Errorf("failed to deliver: %w", err)
|
||||
}
|
||||
deliver = contract.Terms.Deliver[0]
|
||||
if deliver.UnitsRequired == deliver.UnitsFulfilled {
|
||||
if err := a.client.Fulfill(contract, a.db); err != nil {
|
||||
return fmt.Errorf("failed to fulfill: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return a.runProcurement(contract, ship)
|
||||
}
|
||||
|
|
139
golang/pkg/agent/trading.go
Normal file
139
golang/pkg/agent/trading.go
Normal file
|
@ -0,0 +1,139 @@
|
|||
package agent
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
"git.adyxax.org/adyxax/spacetraders/golang/pkg/model"
|
||||
)
|
||||
|
||||
type TradeGoodNotFoundError struct{}
|
||||
|
||||
func (err *TradeGoodNotFoundError) Error() string {
|
||||
return "trade good not found"
|
||||
}
|
||||
|
||||
func (a *agent) buyTradeGood(ship *model.Ship, tradeGoodToBuy string) error {
|
||||
if ship.Cargo.Units == ship.Cargo.Capacity {
|
||||
return nil
|
||||
}
|
||||
// list markets would sell our goods
|
||||
markets, err := a.listMarketsInSystem(ship.Nav.SystemSymbol)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to list markets in system %s: %w", ship.Nav.SystemSymbol, err)
|
||||
}
|
||||
markets = slices.DeleteFunc(markets, func(market model.Market) bool {
|
||||
for _, item := range market.Exports {
|
||||
if item.Symbol == tradeGoodToBuy {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
if len(markets) == 0 {
|
||||
return &TradeGoodNotFoundError{}
|
||||
}
|
||||
// find the closest place to buy TODO
|
||||
waypoint, err := a.client.GetWaypoint(ship.Nav.WaypointSymbol, a.db)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get nav waypoint %s: %w", ship.Nav.WaypointSymbol, err)
|
||||
}
|
||||
waypoints := make([]model.Waypoint, 0)
|
||||
for i := range markets {
|
||||
waypoint, err := a.client.GetWaypoint(markets[i].Symbol, a.db)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get waypoint %s: %w", markets[i].Symbol, err)
|
||||
}
|
||||
waypoints = append(waypoints, *waypoint)
|
||||
}
|
||||
sortByDistanceFrom(waypoint, waypoints)
|
||||
// Go there and refresh our market data
|
||||
if err := a.client.Navigate(ship, waypoints[0].Symbol, a.db); err != nil {
|
||||
return fmt.Errorf("failed to navigate to %s: %w", waypoints[0].Symbol, err)
|
||||
}
|
||||
market, err := a.client.GetMarket(waypoints[0].Symbol, a.db)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get market %s: %w", waypoints[0].Symbol, err)
|
||||
}
|
||||
// Buy until full
|
||||
for _, tradeGood := range market.TradeGoods {
|
||||
if tradeGood.Type == "EXPORT" && tradeGood.Symbol == tradeGoodToBuy {
|
||||
for ship.Cargo.Units < ship.Cargo.Capacity {
|
||||
increment := min(ship.Cargo.Capacity-ship.Cargo.Units, tradeGood.TradeVolume)
|
||||
if err := a.client.Purchase(ship, tradeGoodToBuy, increment, a.db); err != nil {
|
||||
return fmt.Errorf("failed to purchase %d units of %s: %w", increment, tradeGoodToBuy, err)
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return a.buyTradeGood(ship, tradeGoodToBuy)
|
||||
}
|
||||
|
||||
func (a *agent) sellEverythingExcept(ship *model.Ship, keep string) error {
|
||||
// First lets see what we need to sell
|
||||
cargo := ship.Cargo.Inventory
|
||||
cargo = slices.DeleteFunc(cargo, func(inventory model.Inventory) bool {
|
||||
return inventory.Symbol == keep
|
||||
})
|
||||
if len(cargo) == 0 {
|
||||
return nil
|
||||
}
|
||||
// list markets would buy our goods
|
||||
markets, err := a.listMarketsInSystem(ship.Nav.SystemSymbol)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to list markets in system %s: %w", ship.Nav.SystemSymbol, err)
|
||||
}
|
||||
markets = slices.DeleteFunc(markets, func(market model.Market) bool {
|
||||
for _, item := range market.Imports {
|
||||
for _, cargoItem := range cargo {
|
||||
if item.Symbol == cargoItem.Symbol {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
if len(markets) == 0 {
|
||||
return nil
|
||||
}
|
||||
// find the closest place to sell something TODO
|
||||
waypoint, err := a.client.GetWaypoint(ship.Nav.WaypointSymbol, a.db)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get nav waypoint %s: %w", ship.Nav.WaypointSymbol, err)
|
||||
}
|
||||
waypoints := make([]model.Waypoint, 0)
|
||||
for i := range markets {
|
||||
waypoint, err := a.client.GetWaypoint(markets[i].Symbol, a.db)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get waypoint %s: %w", markets[i].Symbol, err)
|
||||
}
|
||||
waypoints = append(waypoints, *waypoint)
|
||||
}
|
||||
sortByDistanceFrom(waypoint, waypoints)
|
||||
// Go there and refresh our market data
|
||||
if err := a.client.Navigate(ship, waypoints[0].Symbol, a.db); err != nil {
|
||||
return fmt.Errorf("failed to navigate to %s: %w", waypoints[0].Symbol, err)
|
||||
}
|
||||
market, err := a.client.GetMarket(waypoints[0].Symbol, a.db)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get market %s: %w", waypoints[0].Symbol, err)
|
||||
}
|
||||
// sell everything we can
|
||||
for _, cargoItem := range cargo {
|
||||
units := cargoItem.Units
|
||||
for _, tradeGood := range market.TradeGoods {
|
||||
if tradeGood.Type == "IMPORT" && tradeGood.Symbol == cargoItem.Symbol {
|
||||
for units > 0 {
|
||||
increment := min(units, tradeGood.TradeVolume)
|
||||
if err := a.client.Sell(ship, cargoItem.Symbol, increment, a.db); err != nil {
|
||||
return fmt.Errorf("failed to sell %d units of %s: %w", units, cargoItem.Symbol, err)
|
||||
}
|
||||
units = units - increment
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return a.sellEverythingExcept(ship, keep)
|
||||
}
|
|
@ -40,6 +40,22 @@ func (a *agent) listWaypointsInSystemWithTrait(systemSymbol string, trait string
|
|||
return waypoints, nil
|
||||
}
|
||||
|
||||
func (a *agent) listMarketsInSystem(systemSymbol string) ([]model.Market, error) {
|
||||
waypoints, err := a.listWaypointsInSystemWithTrait(systemSymbol, "MARKETPLACE")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list waypoints in system %s with trait MARKETPLACE: %w", systemSymbol, err)
|
||||
}
|
||||
var markets []model.Market
|
||||
for i := range waypoints {
|
||||
market, err := a.client.GetMarket(waypoints[i].Symbol, a.db)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get market %s: %w", waypoints[i].Symbol, err)
|
||||
}
|
||||
markets = append(markets, *market)
|
||||
}
|
||||
return markets, nil
|
||||
}
|
||||
|
||||
func (a *agent) listShipyardsInSystem(systemSymbol string) ([]model.Shipyard, error) {
|
||||
waypoints, err := a.listWaypointsInSystemWithTrait(systemSymbol, "SHIPYARD")
|
||||
if err != nil {
|
||||
|
@ -47,7 +63,7 @@ func (a *agent) listShipyardsInSystem(systemSymbol string) ([]model.Shipyard, er
|
|||
}
|
||||
var shipyards []model.Shipyard
|
||||
for i := range waypoints {
|
||||
shipyard, err := a.client.GetShipyard(&waypoints[i], a.db)
|
||||
shipyard, err := a.client.GetShipyard(waypoints[i].Symbol, a.db)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get shipyard %s: %w", waypoints[i].Symbol, err)
|
||||
}
|
||||
|
|
|
@ -39,9 +39,17 @@ func (a *agent) visitAllShipyards(ship *model.Ship) error {
|
|||
if err := a.client.Navigate(ship, waypoints[0].Symbol, a.db); err != nil {
|
||||
return fmt.Errorf("failed to navigate to %s: %w", waypoints[0].Symbol, err)
|
||||
}
|
||||
if _, err := a.client.GetShipyard(&waypoints[0], a.db); err != nil {
|
||||
if _, err := a.client.GetShipyard(waypoints[0].Symbol, a.db); err != nil {
|
||||
return fmt.Errorf("failed to get shipyard %s: %w", waypoints[0].Symbol, err)
|
||||
}
|
||||
// TODO get market data
|
||||
// If this waypoint is also a marketplace, get its data
|
||||
for _, trait := range waypoints[0].Traits {
|
||||
if trait.Symbol == "MARKETPLACE" {
|
||||
if _, err := a.client.GetMarket(waypoints[0].Symbol, a.db); err != nil {
|
||||
return fmt.Errorf("failed to get market %s: %w", waypoints[0].Symbol, err)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return a.visitAllShipyards(ship)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue