diff options
-rw-r--r-- | golang/pkg/agent/run.go | 14 | ||||
-rw-r--r-- | golang/pkg/agent/utils.go | 72 | ||||
-rw-r--r-- | golang/pkg/agent/visit.go | 16 | ||||
-rw-r--r-- | golang/pkg/api/ships.go | 17 | ||||
-rw-r--r-- | golang/pkg/api/systems.go | 36 | ||||
-rw-r--r-- | golang/pkg/database/systems.go | 4 |
6 files changed, 98 insertions, 61 deletions
diff --git a/golang/pkg/agent/run.go b/golang/pkg/agent/run.go index 8767c50..7d7c3a1 100644 --- a/golang/pkg/agent/run.go +++ b/golang/pkg/agent/run.go @@ -23,7 +23,8 @@ type State int const ( start_running_contracts_with_the_command_ship = iota - visit_all_shipyards + visit_all_shipyards_with_the_starting_probe + send_the_starting_probe_to_a_shipyard_that_sells_probes ) func Run( @@ -55,9 +56,16 @@ func Run( agent.wg.Add(1) go agent.autoContracting(&agent.ships[0]) state++ - case visit_all_shipyards: + case visit_all_shipyards_with_the_starting_probe: if err := agent.visitAllShipyards(&agent.ships[1]); err != nil { - agent.channel <- fmt.Errorf("agent runner returned an error on state %d: %w", state, err) + agent.channel <- fmt.Errorf("failed agent run: %w", err) + return + } + state++ + case send_the_starting_probe_to_a_shipyard_that_sells_probes: + if err := agent.sendShipToShipyardThatSells(&agent.ships[1], "SHIP_PROBE"); err != nil { + agent.channel <- fmt.Errorf("failed agent run: %w", err) + return } state++ default: diff --git a/golang/pkg/agent/utils.go b/golang/pkg/agent/utils.go index 5689405..2ad3b44 100644 --- a/golang/pkg/agent/utils.go +++ b/golang/pkg/agent/utils.go @@ -3,33 +3,29 @@ package agent import ( "cmp" "fmt" + "math" "slices" "git.adyxax.org/adyxax/spacetraders/golang/pkg/model" ) -type Point interface { - GetX() int - GetY() int -} - -func distance2(a Point, b Point) int { - x2 := a.GetX() - b.GetX() - y2 := a.GetY() - b.GetY() +func distance2(a *model.Waypoint, b *model.Waypoint) int { + x2 := a.X - b.X + y2 := a.Y - b.Y return x2*x2 + y2*y2 } -func (a *agent) isThereAShipAtWaypoint(waypoint *model.Waypoint) bool { +func (a *agent) isThereAShipAtWaypoint(waypointSymbol string) bool { for _, ship := range a.ships { - if ship.Nav.WaypointSymbol == waypoint.Symbol { + if ship.Nav.WaypointSymbol == waypointSymbol { return true } } return false } -func (a *agent) listWaypointsInSystemWithTrait(system *model.System, trait string) ([]model.Waypoint, error) { - waypoints, err := a.client.ListWaypointsInSystem(system, a.db) +func (a *agent) listWaypointsInSystemWithTrait(systemSymbol string, trait string) ([]model.Waypoint, error) { + waypoints, err := a.client.ListWaypointsInSystem(systemSymbol, a.db) if err != nil { return nil, fmt.Errorf("failed to list waypoints with trait: %w", err) } @@ -44,24 +40,62 @@ func (a *agent) listWaypointsInSystemWithTrait(system *model.System, trait strin return waypoints, nil } -func (a *agent) listShipyardsInSystem(system *model.System) ([]model.Shipyard, error) { - waypoints, err := a.listWaypointsInSystemWithTrait(system, "SHIPYARD") +func (a *agent) listShipyardsInSystem(systemSymbol string) ([]model.Shipyard, error) { + waypoints, err := a.listWaypointsInSystemWithTrait(systemSymbol, "SHIPYARD") if err != nil { - return nil, fmt.Errorf("failed to list shipyards in system: %w", err) + return nil, fmt.Errorf("failed to list shipyards in system %s: %w", systemSymbol, err) } var shipyards []model.Shipyard for i := range waypoints { shipyard, err := a.client.GetShipyard(&waypoints[i], a.db) if err != nil { - return nil, fmt.Errorf("failed to list shipyards in system: %w", err) + return nil, fmt.Errorf("failed to list shipyards in system %s: %w", systemSymbol, err) } shipyards = append(shipyards, *shipyard) } return shipyards, nil } -func sortByDistanceFrom[P Point](origin P, destinations []P) { - slices.SortFunc(destinations, func(a, b P) int { - return cmp.Compare(distance2(origin, a), distance2(origin, b)) +func (a *agent) sendShipToShipyardThatSells(ship *model.Ship, shipType string) error { + shipyards, err := a.listShipyardsInSystem(ship.Nav.SystemSymbol) + if err != nil { + return fmt.Errorf("failed to send ship %s to a shipyard that sells %s: %w", ship.Symbol, shipType, err) + } + // filter out the shipyards that do not sell our ship + shipyards = slices.DeleteFunc(shipyards, func(shipyard model.Shipyard) bool { + for _, t := range shipyard.ShipTypes { + if t.Type == shipType { + return false + } + } + return true + }) + // sort by cheapest + slices.SortFunc(shipyards, func(a, b model.Shipyard) int { + aPrice := math.MaxInt + for _, ship := range a.Ships { + if ship.Type == shipType { + aPrice = ship.PurchasePrice + break + } + } + bPrice := math.MaxInt + for _, ship := range b.Ships { + if ship.Type == shipType { + bPrice = ship.PurchasePrice + break + } + } + return cmp.Compare(aPrice, bPrice) + }) + if err := a.client.Navigate(ship, shipyards[0].Symbol, a.db); err != nil { + return fmt.Errorf("failed to send ship %s to a shipyard that sells %s: %w", ship.Symbol, shipType, err) + } + return nil +} + +func sortByDistanceFrom(origin *model.Waypoint, destinations []model.Waypoint) { + slices.SortFunc(destinations, func(a, b model.Waypoint) int { + return cmp.Compare(distance2(origin, &a), distance2(origin, &b)) }) } diff --git a/golang/pkg/agent/visit.go b/golang/pkg/agent/visit.go index 2401b8d..448b885 100644 --- a/golang/pkg/agent/visit.go +++ b/golang/pkg/agent/visit.go @@ -8,11 +8,7 @@ import ( ) func (a *agent) visitAllShipyards(ship *model.Ship) error { - system, err := a.client.GetSystem(ship.Nav.SystemSymbol, a.db) - if err != nil { - return fmt.Errorf("failed to visit all shipyards: %w", err) - } - shipyards, err := a.listShipyardsInSystem(system) + shipyards, err := a.listShipyardsInSystem(ship.Nav.SystemSymbol) if err != nil { return fmt.Errorf("failed to visit all shipyards: %w", err) } @@ -22,11 +18,7 @@ func (a *agent) visitAllShipyards(ship *model.Ship) error { return true } // filter out shipyards for which a ship is either present or inbound - waypoint, err := a.client.GetWaypoint(shipyard.Symbol, a.db) - if err != nil { - panic(fmt.Errorf("failed to visit all shipyards: %w", err)) - } - return a.isThereAShipAtWaypoint(waypoint) + return a.isThereAShipAtWaypoint(shipyard.Symbol) }) if len(shipyards) == 0 { return nil @@ -43,8 +35,8 @@ func (a *agent) visitAllShipyards(ship *model.Ship) error { } waypoints = append(waypoints, *waypoint) } - sortByDistanceFrom(*waypoint, waypoints) - if err := a.client.Navigate(ship, &waypoints[0], a.db); err != nil { + sortByDistanceFrom(waypoint, waypoints) + if err := a.client.Navigate(ship, waypoints[0].Symbol, a.db); err != nil { return fmt.Errorf("failed to visit all shipyards: %w", err) } if _, err := a.client.GetShipyard(&waypoints[0], a.db); err != nil { diff --git a/golang/pkg/api/ships.go b/golang/pkg/api/ships.go index 16d4e10..83b4e7f 100644 --- a/golang/pkg/api/ships.go +++ b/golang/pkg/api/ships.go @@ -35,12 +35,15 @@ func (c *Client) MyShips() ([]model.Ship, error) { return ships, nil } -func (c *Client) Navigate(s *model.Ship, w *model.Waypoint, db *database.DB) error { - // TODO shortest path - // TODO go refuel if necessary +func (c *Client) Navigate(s *model.Ship, waypointSymbol string, db *database.DB) error { + if s.Nav.WaypointSymbol == waypointSymbol { + return nil + } if err := c.orbit(s); err != nil { - return fmt.Errorf("failed to navigate ship %s to %s: %w", s.Symbol, w.Symbol, err) + return fmt.Errorf("failed to navigate ship %s to %s: %w", s.Symbol, waypointSymbol, err) } + // TODO shortest path + // TODO go refuel if necessary uriRef := url.URL{Path: path.Join("my/ships", s.Symbol, "navigate")} type navigateRequest struct { WaypointSymbol string `json:"waypointSymbol"` @@ -51,14 +54,14 @@ func (c *Client) Navigate(s *model.Ship, w *model.Waypoint, db *database.DB) err Nav *model.Nav `json:"nav"` } var response navigateResponse - if err := c.Send("POST", &uriRef, navigateRequest{w.Symbol}, &response); err != nil { - return fmt.Errorf("failed to navigate ship %s to %s: %w", s.Symbol, w.Symbol, err) + if err := c.Send("POST", &uriRef, navigateRequest{waypointSymbol}, &response); err != nil { + return fmt.Errorf("failed to navigate ship %s to %s: %w", s.Symbol, waypointSymbol, err) } s.Fuel = response.Fuel s.Nav = response.Nav select { case <-c.ctx.Done(): - return fmt.Errorf("failed to navigate ship %s to %s: ctx cancelled", s.Symbol, w.Symbol) + return fmt.Errorf("failed to navigate ship %s to %s: ctx cancelled", s.Symbol, waypointSymbol) case <-time.After(s.Nav.Route.Arrival.Sub(time.Now())): } s.Nav.Status = "IN_ORBIT" diff --git a/golang/pkg/api/systems.go b/golang/pkg/api/systems.go index 05e43e3..5f5def6 100644 --- a/golang/pkg/api/systems.go +++ b/golang/pkg/api/systems.go @@ -24,24 +24,6 @@ func (c *Client) GetSystem(symbol string, db *database.DB) (*model.System, error return &system, nil } -func (c *Client) ListWaypointsInSystem(system *model.System, db *database.DB) ([]model.Waypoint, error) { - if waypoints, err := db.LoadWaypointsInSystem(system); err == nil && waypoints != nil { - // TODO check last updated time - return waypoints, nil - } - uriRef := url.URL{Path: path.Join("systems", system.Symbol, "waypoints")} - var waypoints []model.Waypoint - if err := c.Send("GET", &uriRef, nil, &waypoints); err != nil { - return nil, fmt.Errorf("failed to list waypoints in system %s: %w", system.Symbol, err) - } - for _, waypoint := range waypoints { - if err := db.SaveWaypoint(&waypoint); err != nil { - return nil, fmt.Errorf("failed to list waypoints in system %s: %w", system.Symbol, err) - } - } - return waypoints, nil -} - func (c *Client) GetShipyard(waypoint *model.Waypoint, db *database.DB) (*model.Shipyard, error) { if shipyard, err := db.LoadShipyard(waypoint.Symbol); err == nil && shipyard != nil && (shipyard.Ships != nil) { // TODO || !IsThereAShipAtWaypoint(waypoint)) { @@ -75,3 +57,21 @@ func (c *Client) GetWaypoint(symbol string, db *database.DB) (*model.Waypoint, e } return &waypoint, nil } + +func (c *Client) ListWaypointsInSystem(systemSymbol string, db *database.DB) ([]model.Waypoint, error) { + if waypoints, err := db.LoadWaypointsInSystem(systemSymbol); err == nil && waypoints != nil { + // TODO check last updated time + return waypoints, nil + } + uriRef := url.URL{Path: path.Join("systems", systemSymbol, "waypoints")} + var waypoints []model.Waypoint + if err := c.Send("GET", &uriRef, nil, &waypoints); err != nil { + return nil, fmt.Errorf("failed to list waypoints in system %s: %w", systemSymbol, err) + } + for _, waypoint := range waypoints { + if err := db.SaveWaypoint(&waypoint); err != nil { + return nil, fmt.Errorf("failed to list waypoints in system %s: %w", systemSymbol, err) + } + } + return waypoints, nil +} diff --git a/golang/pkg/database/systems.go b/golang/pkg/database/systems.go index 0af6aa7..92869db 100644 --- a/golang/pkg/database/systems.go +++ b/golang/pkg/database/systems.go @@ -45,8 +45,8 @@ func (db *DB) LoadWaypoint(symbol string) (*model.Waypoint, error) { return &waypoint, nil } -func (db *DB) LoadWaypointsInSystem(system *model.System) ([]model.Waypoint, error) { - rows, err := db.Query(`SELECT data FROM waypoints WHERE data->>'systemSymbol' = ?;`, system.Symbol) +func (db *DB) LoadWaypointsInSystem(systemSymbol string) ([]model.Waypoint, error) { + rows, err := db.Query(`SELECT data FROM waypoints WHERE data->>'systemSymbol' = ?;`, systemSymbol) if err != nil { return nil, fmt.Errorf("failed to query waypoints: %w", err) } |