[golang] implement sending the starting probe to a shipyard that sells other probes
This commit is contained in:
parent
bd2fb50c81
commit
3656b87b86
6 changed files with 98 additions and 61 deletions
|
@ -23,7 +23,8 @@ type State int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
start_running_contracts_with_the_command_ship = iota
|
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(
|
func Run(
|
||||||
|
@ -55,9 +56,16 @@ func Run(
|
||||||
agent.wg.Add(1)
|
agent.wg.Add(1)
|
||||||
go agent.autoContracting(&agent.ships[0])
|
go agent.autoContracting(&agent.ships[0])
|
||||||
state++
|
state++
|
||||||
case visit_all_shipyards:
|
case visit_all_shipyards_with_the_starting_probe:
|
||||||
if err := agent.visitAllShipyards(&agent.ships[1]); err != nil {
|
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++
|
state++
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -3,33 +3,29 @@ package agent
|
||||||
import (
|
import (
|
||||||
"cmp"
|
"cmp"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
"slices"
|
"slices"
|
||||||
|
|
||||||
"git.adyxax.org/adyxax/spacetraders/golang/pkg/model"
|
"git.adyxax.org/adyxax/spacetraders/golang/pkg/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Point interface {
|
func distance2(a *model.Waypoint, b *model.Waypoint) int {
|
||||||
GetX() int
|
x2 := a.X - b.X
|
||||||
GetY() int
|
y2 := a.Y - b.Y
|
||||||
}
|
|
||||||
|
|
||||||
func distance2(a Point, b Point) int {
|
|
||||||
x2 := a.GetX() - b.GetX()
|
|
||||||
y2 := a.GetY() - b.GetY()
|
|
||||||
return x2*x2 + y2*y2
|
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 {
|
for _, ship := range a.ships {
|
||||||
if ship.Nav.WaypointSymbol == waypoint.Symbol {
|
if ship.Nav.WaypointSymbol == waypointSymbol {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *agent) listWaypointsInSystemWithTrait(system *model.System, trait string) ([]model.Waypoint, error) {
|
func (a *agent) listWaypointsInSystemWithTrait(systemSymbol string, trait string) ([]model.Waypoint, error) {
|
||||||
waypoints, err := a.client.ListWaypointsInSystem(system, a.db)
|
waypoints, err := a.client.ListWaypointsInSystem(systemSymbol, a.db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to list waypoints with trait: %w", err)
|
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
|
return waypoints, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *agent) listShipyardsInSystem(system *model.System) ([]model.Shipyard, error) {
|
func (a *agent) listShipyardsInSystem(systemSymbol string) ([]model.Shipyard, error) {
|
||||||
waypoints, err := a.listWaypointsInSystemWithTrait(system, "SHIPYARD")
|
waypoints, err := a.listWaypointsInSystemWithTrait(systemSymbol, "SHIPYARD")
|
||||||
if err != nil {
|
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
|
var shipyards []model.Shipyard
|
||||||
for i := range waypoints {
|
for i := range waypoints {
|
||||||
shipyard, err := a.client.GetShipyard(&waypoints[i], a.db)
|
shipyard, err := a.client.GetShipyard(&waypoints[i], a.db)
|
||||||
if err != nil {
|
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)
|
shipyards = append(shipyards, *shipyard)
|
||||||
}
|
}
|
||||||
return shipyards, nil
|
return shipyards, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func sortByDistanceFrom[P Point](origin P, destinations []P) {
|
func (a *agent) sendShipToShipyardThatSells(ship *model.Ship, shipType string) error {
|
||||||
slices.SortFunc(destinations, func(a, b P) int {
|
shipyards, err := a.listShipyardsInSystem(ship.Nav.SystemSymbol)
|
||||||
return cmp.Compare(distance2(origin, a), distance2(origin, b))
|
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))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,11 +8,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (a *agent) visitAllShipyards(ship *model.Ship) error {
|
func (a *agent) visitAllShipyards(ship *model.Ship) error {
|
||||||
system, err := a.client.GetSystem(ship.Nav.SystemSymbol, a.db)
|
shipyards, err := a.listShipyardsInSystem(ship.Nav.SystemSymbol)
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to visit all shipyards: %w", err)
|
|
||||||
}
|
|
||||||
shipyards, err := a.listShipyardsInSystem(system)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to visit all shipyards: %w", err)
|
return fmt.Errorf("failed to visit all shipyards: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -22,11 +18,7 @@ func (a *agent) visitAllShipyards(ship *model.Ship) error {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// filter out shipyards for which a ship is either present or inbound
|
// filter out shipyards for which a ship is either present or inbound
|
||||||
waypoint, err := a.client.GetWaypoint(shipyard.Symbol, a.db)
|
return a.isThereAShipAtWaypoint(shipyard.Symbol)
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Errorf("failed to visit all shipyards: %w", err))
|
|
||||||
}
|
|
||||||
return a.isThereAShipAtWaypoint(waypoint)
|
|
||||||
})
|
})
|
||||||
if len(shipyards) == 0 {
|
if len(shipyards) == 0 {
|
||||||
return nil
|
return nil
|
||||||
|
@ -43,8 +35,8 @@ func (a *agent) visitAllShipyards(ship *model.Ship) error {
|
||||||
}
|
}
|
||||||
waypoints = append(waypoints, *waypoint)
|
waypoints = append(waypoints, *waypoint)
|
||||||
}
|
}
|
||||||
sortByDistanceFrom(*waypoint, waypoints)
|
sortByDistanceFrom(waypoint, waypoints)
|
||||||
if err := a.client.Navigate(ship, &waypoints[0], a.db); err != nil {
|
if err := a.client.Navigate(ship, waypoints[0].Symbol, a.db); err != nil {
|
||||||
return fmt.Errorf("failed to visit all shipyards: %w", err)
|
return fmt.Errorf("failed to visit all shipyards: %w", err)
|
||||||
}
|
}
|
||||||
if _, err := a.client.GetShipyard(&waypoints[0], a.db); err != nil {
|
if _, err := a.client.GetShipyard(&waypoints[0], a.db); err != nil {
|
||||||
|
|
|
@ -35,12 +35,15 @@ func (c *Client) MyShips() ([]model.Ship, error) {
|
||||||
return ships, nil
|
return ships, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) Navigate(s *model.Ship, w *model.Waypoint, db *database.DB) error {
|
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, waypointSymbol, err)
|
||||||
|
}
|
||||||
// TODO shortest path
|
// TODO shortest path
|
||||||
// TODO go refuel if necessary
|
// TODO go refuel if necessary
|
||||||
if err := c.orbit(s); err != nil {
|
|
||||||
return fmt.Errorf("failed to navigate ship %s to %s: %w", s.Symbol, w.Symbol, err)
|
|
||||||
}
|
|
||||||
uriRef := url.URL{Path: path.Join("my/ships", s.Symbol, "navigate")}
|
uriRef := url.URL{Path: path.Join("my/ships", s.Symbol, "navigate")}
|
||||||
type navigateRequest struct {
|
type navigateRequest struct {
|
||||||
WaypointSymbol string `json:"waypointSymbol"`
|
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"`
|
Nav *model.Nav `json:"nav"`
|
||||||
}
|
}
|
||||||
var response navigateResponse
|
var response navigateResponse
|
||||||
if err := c.Send("POST", &uriRef, navigateRequest{w.Symbol}, &response); err != nil {
|
if err := c.Send("POST", &uriRef, navigateRequest{waypointSymbol}, &response); 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)
|
||||||
}
|
}
|
||||||
s.Fuel = response.Fuel
|
s.Fuel = response.Fuel
|
||||||
s.Nav = response.Nav
|
s.Nav = response.Nav
|
||||||
select {
|
select {
|
||||||
case <-c.ctx.Done():
|
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())):
|
case <-time.After(s.Nav.Route.Arrival.Sub(time.Now())):
|
||||||
}
|
}
|
||||||
s.Nav.Status = "IN_ORBIT"
|
s.Nav.Status = "IN_ORBIT"
|
||||||
|
|
|
@ -24,24 +24,6 @@ func (c *Client) GetSystem(symbol string, db *database.DB) (*model.System, error
|
||||||
return &system, nil
|
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) {
|
func (c *Client) GetShipyard(waypoint *model.Waypoint, db *database.DB) (*model.Shipyard, error) {
|
||||||
if shipyard, err := db.LoadShipyard(waypoint.Symbol); err == nil && shipyard != nil &&
|
if shipyard, err := db.LoadShipyard(waypoint.Symbol); err == nil && shipyard != nil &&
|
||||||
(shipyard.Ships != nil) { // TODO || !IsThereAShipAtWaypoint(waypoint)) {
|
(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
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -45,8 +45,8 @@ func (db *DB) LoadWaypoint(symbol string) (*model.Waypoint, error) {
|
||||||
return &waypoint, nil
|
return &waypoint, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) LoadWaypointsInSystem(system *model.System) ([]model.Waypoint, error) {
|
func (db *DB) LoadWaypointsInSystem(systemSymbol string) ([]model.Waypoint, error) {
|
||||||
rows, err := db.Query(`SELECT data FROM waypoints WHERE data->>'systemSymbol' = ?;`, system.Symbol)
|
rows, err := db.Query(`SELECT data FROM waypoints WHERE data->>'systemSymbol' = ?;`, systemSymbol)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to query waypoints: %w", err)
|
return nil, fmt.Errorf("failed to query waypoints: %w", err)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue