tests: produce on planets, unload on routes
This commit is contained in:
@@ -41,7 +41,7 @@ func (c *Cache) SetRoute(ri int, rt game.RouteType, origin, destination uint) er
|
||||
}
|
||||
rangeToDestination := util.ShortDistance(c.g.Map.Width, c.g.Map.Height, p1.X, p1.Y, p2.X, p2.Y)
|
||||
if rangeToDestination > c.g.Race[ri].FlightDistance() {
|
||||
return e.NewSendUnreachableDestinationError("range=%.03f", rangeToDestination)
|
||||
return e.NewSendUnreachableDestinationError("range=%.03f max=%.03f", rangeToDestination, c.g.Race[ri].FlightDistance())
|
||||
}
|
||||
|
||||
c.SetPlanetRoute(rt, origin, destination)
|
||||
@@ -97,7 +97,7 @@ func (c *Cache) EnrouteGroups() {
|
||||
if len(c.g.Map.Planet[pi].Route) == 0 {
|
||||
continue
|
||||
}
|
||||
groups := slices.Collect(c.listRouteEligibleGroupIds(c.g.Map.Planet[pi].Number))
|
||||
groups := slices.Collect(c.listRoutedSendGroupIds(c.g.Map.Planet[pi].Number))
|
||||
if len(groups) == 0 {
|
||||
continue
|
||||
}
|
||||
@@ -171,7 +171,7 @@ func (c *Cache) EnrouteGroups() {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cache) listRouteEligibleGroupIds(pn uint) iter.Seq[int] {
|
||||
func (c *Cache) listRoutedSendGroupIds(pn uint) iter.Seq[int] {
|
||||
return func(yield func(int) bool) {
|
||||
p := c.MustPlanet(pn)
|
||||
for i := range c.ShipGroupsIndex() {
|
||||
@@ -194,63 +194,83 @@ func (c *Cache) listRouteEligibleGroupIds(pn uint) iter.Seq[int] {
|
||||
|
||||
// Невозможно лишь выгрузить колонистов на чужой планете.
|
||||
func (c *Cache) TurnUnloadEnroutedGroups() {
|
||||
for pi := range c.g.Map.Planet {
|
||||
p := &c.g.Map.Planet[pi]
|
||||
colGroups := c.listUnloadEligibleShipGroupIds(p.Number, game.RouteColonist)
|
||||
if p.Owner == uuid.Nil {
|
||||
c.selectColUnloadGroup(colGroups)
|
||||
} else {
|
||||
for sgi := range colGroups {
|
||||
sg := c.ShipGroup(sgi)
|
||||
if sg.OwnerID != p.Owner {
|
||||
continue
|
||||
}
|
||||
c.unloadCargoUnsafe(sgi, sg.Load)
|
||||
}
|
||||
}
|
||||
for i := range c.g.Map.Planet {
|
||||
p := &c.g.Map.Planet[i]
|
||||
c.doUnload(c.unloadRoutedColonists(p.Number, c.listRoutedUnloadShipGroupIds(p.Number, game.RouteColonist)))
|
||||
for _, rt := range []game.RouteType{game.RouteMaterial, game.RouteCapital} {
|
||||
for sgi := range c.listUnloadEligibleShipGroupIds(p.Number, rt) {
|
||||
c.unloadCargoUnsafe(sgi, c.ShipGroup(sgi).Load)
|
||||
}
|
||||
c.doUnload(c.listRoutedUnloadShipGroupIds(p.Number, rt))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cache) selectColUnloadGroup(seq iter.Seq[int]) {
|
||||
func (c *Cache) doUnload(groups iter.Seq[int]) {
|
||||
for sgi := range groups {
|
||||
c.unsafeUnloadCargo(sgi, c.ShipGroup(sgi).Load)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cache) unloadRoutedColonists(pn uint, groups iter.Seq[int]) iter.Seq[int] {
|
||||
p := c.MustPlanet(pn)
|
||||
gr := slices.Collect(groups)
|
||||
if p.Owner == uuid.Nil {
|
||||
return c.selectColUnloadGroup(gr)
|
||||
}
|
||||
return func(yield func(int) bool) {
|
||||
for _, sgi := range gr {
|
||||
sg := c.ShipGroup(sgi)
|
||||
if p.Owner != sg.OwnerID {
|
||||
continue
|
||||
}
|
||||
if !yield(sgi) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (c *Cache) selectColUnloadGroup(groups []int) (result iter.Seq[int]) {
|
||||
groupByRace := make(map[int][]int)
|
||||
loadByRace := make(map[int]float64)
|
||||
for i := range seq {
|
||||
for _, i := range groups {
|
||||
sg := c.ShipGroup(i)
|
||||
ri := c.RaceIndex(sg.OwnerID)
|
||||
groupByRace[ri] = append(groupByRace[ri], i)
|
||||
loadByRace[ri] += sg.Load
|
||||
}
|
||||
if len(loadByRace) < 2 {
|
||||
for _, gr := range groupByRace {
|
||||
for _, sgi := range gr {
|
||||
c.unloadCargoUnsafe(sgi, c.ShipGroup(sgi).Load)
|
||||
}
|
||||
}
|
||||
// only one race has to unload cargo
|
||||
result = slices.Values(groups)
|
||||
return
|
||||
}
|
||||
|
||||
// select winner to unload
|
||||
// select winner to unload cargo
|
||||
id := MaxOrRandomLoadId(loadByRace)
|
||||
result = slices.Values(groupByRace[id])
|
||||
|
||||
raceIdx := slices.Collect(maps.Keys(loadByRace))
|
||||
slices.SortFunc(raceIdx, func(ri1, ri2 int) int { return cmp.Compare(loadByRace[ri2], loadByRace[ri1]) })
|
||||
if loadByRace[raceIdx[0]] == loadByRace[raceIdx[1]] {
|
||||
// no single winner with highest load
|
||||
raceIdx = slices.DeleteFunc(raceIdx, func(v int) bool { return loadByRace[v] < loadByRace[raceIdx[0]] })
|
||||
rand.Shuffle(len(raceIdx), func(i, j int) { raceIdx[i], raceIdx[j] = raceIdx[j], raceIdx[i] })
|
||||
// now raceIdx[0] has a random race index
|
||||
}
|
||||
for _, sgi := range groupByRace[raceIdx[0]] {
|
||||
c.unloadCargoUnsafe(sgi, c.ShipGroup(sgi).Load)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Cache) listUnloadEligibleShipGroupIds(pn uint, routeType game.RouteType) iter.Seq[int] {
|
||||
func MaxOrRandomLoadId(IDtoLoad map[int]float64) int {
|
||||
if len(IDtoLoad) < 2 {
|
||||
panic("IDtoLoad must contain at least 2 keys")
|
||||
}
|
||||
IDs := slices.Collect(maps.Keys(IDtoLoad))
|
||||
slices.SortFunc(IDs, func(id1, id2 int) int { return cmp.Compare(IDtoLoad[id2], IDtoLoad[id1]) })
|
||||
|
||||
// no single winner with highest load
|
||||
if IDtoLoad[IDs[0]] == IDtoLoad[IDs[1]] {
|
||||
// remove IDs which load less than maximum
|
||||
IDs = slices.DeleteFunc(IDs, func(v int) bool { return IDtoLoad[v] < IDtoLoad[IDs[0]] })
|
||||
// IDs[0] will have random index
|
||||
rand.Shuffle(len(IDs), func(i, j int) { IDs[i], IDs[j] = IDs[j], IDs[i] })
|
||||
}
|
||||
return IDs[0]
|
||||
}
|
||||
|
||||
func (c *Cache) listRoutedUnloadShipGroupIds(pn uint, routeType game.RouteType) iter.Seq[int] {
|
||||
return func(yield func(int) bool) {
|
||||
yielded := make(map[int]bool)
|
||||
for i := range c.g.Map.Planet {
|
||||
for rt, dest := range c.g.Map.Planet[i].Route {
|
||||
if dest != pn || rt != routeType {
|
||||
@@ -258,12 +278,16 @@ func (c *Cache) listUnloadEligibleShipGroupIds(pn uint, routeType game.RouteType
|
||||
}
|
||||
for i := range c.ShipGroupsIndex() {
|
||||
sg := c.ShipGroup(i)
|
||||
if sg.FleetID != nil || sg.State() != game.StateInOrbit || sg.CargoType == nil {
|
||||
if _, ok := yielded[i]; ok || sg.FleetID != nil || sg.CargoType == nil || sg.Load == 0. || sg.State() != game.StateInOrbit || sg.Destination != dest {
|
||||
continue
|
||||
}
|
||||
if v, ok := game.RouteToCargo[rt]; !ok || v != *sg.CargoType {
|
||||
continue
|
||||
}
|
||||
if !yield(i) {
|
||||
return
|
||||
}
|
||||
yielded[i] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user