race quit, transfer state, refactor

This commit is contained in:
Ilia Denisov
2026-02-07 01:59:11 +02:00
parent 43ba5eb07c
commit fc73cbf83a
27 changed files with 520 additions and 341 deletions
+1 -1
View File
@@ -43,7 +43,7 @@ func (c *Cache) ShipGroupShipClass(groupIndex int) *game.ShipType {
func (c *Cache) RaceIndex(ID uuid.UUID) int {
if c.cacheRaceIndexByID == nil {
c.cacheRaceIndexByID = make(map[uuid.UUID]int)
for i := range c.g.Race {
for i := range c.listRaceIdx() {
c.cacheRaceIndexByID[c.g.Race[i].ID] = i
}
}
+262
View File
@@ -0,0 +1,262 @@
package controller
import (
"strings"
e "github.com/iliadenisov/galaxy/internal/error"
"github.com/iliadenisov/galaxy/internal/model/game"
)
func (c Controller) QuitGame(actor string) error {
ri, err := c.Cache.validActor(actor)
if err != nil {
return err
}
c.Cache.g.Race[ri].TTL = 3
return nil
}
func (c Controller) GiveVotes(actor, acceptor string) error {
ri, err := c.Cache.validActor(actor)
if err != nil {
return err
}
rec, err := c.Cache.validRace(acceptor)
if err != nil {
return err
}
c.Cache.g.Race[ri].VoteFor = c.Cache.g.Race[rec].ID
return nil
}
// FIXME: remove, not a command
func (c Controller) Relation(actor, acceptor string) (game.Relation, error) {
r1, err := c.Cache.validActor(actor)
if err != nil {
return game.Relation(""), err
}
r2, err := c.Cache.validRace(acceptor)
if err != nil {
return game.Relation(""), err
}
return c.Cache.Relation(r1, r2), nil
}
func (c Controller) UpdateRelation(actor, acceptor string, rel game.Relation) error {
ri, err := c.Cache.validActor(actor)
if err != nil {
return err
}
var other int
if actor == acceptor {
other = ri
} else if other, err = c.Cache.validRace(acceptor); err != nil {
return err
}
if err != nil {
return err
}
return c.Cache.UpdateRelation(ri, other, rel)
}
func (c *Controller) JoinShipGroupToFleet(actor, fleetName string, group, count uint) error {
ri, err := c.Cache.validActor(actor)
if err != nil {
return err
}
return c.Cache.JoinShipGroupToFleet(ri, fleetName, group, count)
}
func (c *Controller) JoinFleets(actor, fleetSourceName, fleetTargetName string) error {
ri, err := c.Cache.validActor(actor)
if err != nil {
return err
}
return c.Cache.JoinFleets(ri, fleetSourceName, fleetTargetName)
}
func (c *Controller) SendFleet(actor, fleetName string, planetNumber uint) error {
ri, err := c.Cache.validActor(actor)
if err != nil {
return err
}
fi, ok := c.Cache.fleetIndex(ri, fleetName)
if !ok {
return e.NewEntityNotExistsError("fleet %q", fleetName)
}
return c.Cache.SendFleet(ri, fi, planetNumber)
}
func (c *Controller) RenamePlanet(actor string, planetNumber int, typeName string) error {
ri, err := c.Cache.validActor(actor)
if err != nil {
return err
}
return c.Cache.RenamePlanet(ri, planetNumber, typeName)
}
func (c *Controller) PlanetProduction(actor string, planetNumber int, prodType, subject string) error {
ri, err := c.Cache.validActor(actor)
if err != nil {
return err
}
var prod game.ProductionType
switch game.ProductionType(strings.ToUpper(prodType)) {
case game.ProductionMaterial:
prod = game.ProductionMaterial
case game.ProductionCapital:
prod = game.ProductionCapital
case game.ResearchDrive:
prod = game.ResearchDrive
case game.ResearchWeapons:
prod = game.ResearchWeapons
case game.ResearchShields:
prod = game.ResearchShields
case game.ResearchCargo:
prod = game.ResearchCargo
case game.ResearchScience:
prod = game.ResearchScience
case game.ProductionShip:
prod = game.ProductionShip
default:
return e.NewProductionInvalidError(prodType)
}
return c.Cache.PlanetProduction(ri, planetNumber, prod, subject)
}
func (c *Controller) SetRoute(actor, loadType string, origin, destination uint) error {
ri, err := c.Cache.validActor(actor)
if err != nil {
return err
}
rt, ok := game.RouteTypeSet[loadType]
if !ok {
return e.NewCargoTypeInvalidError(loadType)
}
return c.Cache.SetRoute(ri, rt, origin, destination)
}
func (c *Controller) RemoveRoute(actor, loadType string, origin uint) error {
ri, err := c.Cache.validActor(actor)
if err != nil {
return err
}
rt, ok := game.RouteTypeSet[loadType]
if !ok {
return e.NewCargoTypeInvalidError(loadType)
}
return c.Cache.RemoveRoute(ri, rt, origin)
}
func (c *Controller) CreateScience(actor, typeName string, drive, weapons, shields, cargo float64) error {
ri, err := c.Cache.validActor(actor)
if err != nil {
return err
}
return c.Cache.CreateScience(ri, typeName, drive, weapons, shields, cargo)
}
func (c *Controller) DeleteScience(actor, typeName string) error {
ri, err := c.Cache.validActor(actor)
if err != nil {
return err
}
return c.Cache.DeleteScience(ri, typeName)
}
func (c *Controller) CreateShipType(actor, typeName string, drive float64, ammo int, weapons, shileds, cargo float64) error {
ri, err := c.Cache.validActor(actor)
if err != nil {
return err
}
return c.Cache.CreateShipType(ri, typeName, drive, ammo, weapons, shileds, cargo)
}
func (c *Controller) MergeShipType(actor, name, targetName string) error {
ri, err := c.Cache.validActor(actor)
if err != nil {
return err
}
return c.Cache.MergeShipType(ri, name, targetName)
}
func (c *Controller) DeleteShipType(actor, typeName string) error {
ri, err := c.Cache.validActor(actor)
if err != nil {
return err
}
return c.Cache.DeleteShipType(ri, typeName)
}
func (c *Controller) SendGroup(actor string, groupIndex, planetNumber, quantity uint) error {
ri, err := c.Cache.validActor(actor)
if err != nil {
return err
}
return c.Cache.SendGroup(ri, groupIndex, planetNumber, quantity)
}
func (c *Controller) UpgradeGroup(actor string, groupIndex uint, techInput string, limitShips uint, limitLevel float64) error {
ri, err := c.Cache.validActor(actor)
if err != nil {
return err
}
return c.Cache.UpgradeGroup(ri, groupIndex, techInput, limitShips, limitLevel)
}
func (c *Controller) JoinEqualGroups(actor string) error {
ri, err := c.Cache.validActor(actor)
if err != nil {
return err
}
c.Cache.JoinEqualGroups(ri)
return nil
}
func (c *Controller) BreakGroup(actor string, groupIndex, quantity uint) error {
ri, err := c.Cache.validActor(actor)
if err != nil {
return err
}
return c.Cache.BreakGroup(ri, groupIndex, quantity)
}
func (c *Controller) DisassembleGroup(actor string, groupIndex, quantity uint) error {
ri, err := c.Cache.validActor(actor)
if err != nil {
return err
}
return c.Cache.DisassembleGroup(ri, groupIndex, quantity)
}
func (c *Controller) LoadCargo(actor string, groupIndex uint, cargoType string, ships uint, quantity float64) error {
ri, err := c.Cache.validActor(actor)
if err != nil {
return err
}
ct, ok := game.CargoTypeSet[cargoType]
if !ok {
return e.NewCargoTypeInvalidError(cargoType)
}
return c.Cache.LoadCargo(ri, groupIndex, ct, ships, quantity)
}
func (c *Controller) UnloadCargo(actor string, groupIndex uint, ships uint, quantity float64) error {
ri, err := c.Cache.validActor(actor)
if err != nil {
return err
}
return c.Cache.UnloadCargo(ri, groupIndex, ships, quantity)
}
func (c *Controller) TransferGroup(actor, acceptor string, groupIndex, quantity uint) error {
ri, err := c.Cache.validActor(actor)
if err != nil {
return err
}
riAccept, err := c.Cache.validRace(acceptor)
if err != nil {
return err
}
return c.Cache.TransferGroup(ri, riAccept, groupIndex, quantity)
}
@@ -18,7 +18,7 @@ func (c *Cache) AddRace(n string) (int, uuid.UUID) {
Relations: make([]game.RaceRelation, len(c.g.Race)),
}
c.g.Race = append(c.g.Race, *r)
for i := range c.g.Race {
for i := range c.listRaceIdx() {
if c.g.Race[i].ID != id {
c.g.Race[i].Relations = append(c.g.Race[i].Relations, game.RaceRelation{RaceID: id, Relation: game.RelationPeace})
continue
+2
View File
@@ -16,6 +16,7 @@ var (
ID: Race_0_ID,
VoteFor: Race_0_ID,
Name: "Race_0",
TTL: 10,
Tech: map[game.Tech]game.Float{
game.TechDrive: 1.1,
game.TechWeapons: 1.2,
@@ -28,6 +29,7 @@ var (
ID: Race_1_ID,
VoteFor: Race_1_ID,
Name: "Race_1",
TTL: 10,
Tech: map[game.Tech]game.Float{
game.TechDrive: 2.1,
game.TechWeapons: 2.2,
+21 -24
View File
@@ -100,14 +100,6 @@ func (c *Cache) FleetSpeedAndMass(fi int) (float64, float64) {
return speed, mass
}
func (c *Controller) JoinShipGroupToFleet(raceName, fleetName string, group, count uint) error {
ri, err := c.Cache.raceIndex(raceName)
if err != nil {
return err
}
return c.Cache.JoinShipGroupToFleet(ri, fleetName, group, count)
}
func (c *Cache) JoinShipGroupToFleet(ri int, fleetName string, groupIndex, quantity uint) (err error) {
c.validateRaceIndex(ri)
name, ok := util.ValidateTypeName(fleetName)
@@ -119,8 +111,8 @@ func (c *Cache) JoinShipGroupToFleet(ri int, fleetName string, groupIndex, quant
return e.NewEntityNotExistsError("group #%d", groupIndex)
}
if c.ShipGroup(sgi).State() != game.StateInOrbit {
return e.NewShipsBusyError()
if state := c.ShipGroup(sgi).State(); state != game.StateInOrbit {
return e.NewShipsBusyError("state: %s", state)
}
if c.ShipGroup(sgi).Number < quantity {
@@ -178,14 +170,6 @@ func (c *Cache) JoinShipGroupToFleet(ri int, fleetName string, groupIndex, quant
return nil
}
func (c *Controller) JoinFleets(raceName, fleetSourceName, fleetTargetName string) error {
ri, err := c.Cache.raceIndex(raceName)
if err != nil {
return err
}
return c.Cache.JoinFleets(ri, fleetSourceName, fleetTargetName)
}
func (c *Cache) JoinFleets(ri int, fleetSourceName, fleetTargetName string) (err error) {
fiSource, ok := c.fleetIndex(ri, fleetSourceName)
if !ok {
@@ -242,19 +226,21 @@ func (c *Cache) deleteFleetSafe(ri int, name string) error {
return e.NewEntityInUseError("fleet %s: race %s, group #%d", name, c.g.Race[ri].Name, sg.Number)
}
}
c.g.Fleets = append(c.g.Fleets[:fi], c.g.Fleets[fi+1:]...)
c.cacheFleetIndexByID = nil
c.unsafeDeleteFleet(fi)
return nil
}
func (c *Cache) unsafeDeleteFleet(fi int) {
c.validateFleetIndex(fi)
c.g.Fleets = append(c.g.Fleets[:fi], c.g.Fleets[fi+1:]...)
c.invalidateFleetCache()
}
// Internal funcs
func (c *Cache) FleetIndex(ID uuid.UUID) (int, bool) {
if len(c.cacheFleetIndexByID) == 0 {
c.cacheFleetIndexByID = make(map[uuid.UUID]int)
for i := range c.g.Fleets {
c.cacheFleetIndexByID[c.g.Fleets[i].ID] = i
}
c.cacheFleetIndex()
}
if v, ok := c.cacheFleetIndexByID[ID]; ok {
return v, true
@@ -263,6 +249,17 @@ func (c *Cache) FleetIndex(ID uuid.UUID) (int, bool) {
}
}
func (c *Cache) cacheFleetIndex() {
if c.cacheFleetIndexByID != nil {
clear(c.cacheFleetIndexByID)
} else {
c.cacheFleetIndexByID = make(map[uuid.UUID]int)
}
for i := range c.g.Fleets {
c.cacheFleetIndexByID[c.g.Fleets[i].ID] = i
}
}
func (c *Cache) MustFleetIndex(ID uuid.UUID) int {
if v, ok := c.FleetIndex(ID); ok {
return v
+1 -13
View File
@@ -6,25 +6,13 @@ import (
"github.com/iliadenisov/galaxy/internal/util"
)
func (c *Controller) SendFleet(raceName, fleetName string, planetNumber uint) error {
ri, err := c.Cache.raceIndex(raceName)
if err != nil {
return err
}
fi, ok := c.Cache.fleetIndex(ri, fleetName)
if !ok {
return e.NewEntityNotExistsError("fleet %q", fleetName)
}
return c.Cache.SendFleet(ri, fi, planetNumber)
}
func (c *Cache) SendFleet(ri, fi int, planetNumber uint) error {
c.validateRaceIndex(ri)
c.validateFleetIndex(fi)
fleetState := c.FleetState(c.g.Fleets[fi].ID)
sourcePlanet, ok := fleetState.AtPlanet()
if !ok || game.StateInOrbit != fleetState.State && game.StateLaunched != fleetState.State {
return e.NewShipsBusyError()
return e.NewShipsBusyError("state: %s", fleetState.State)
}
p1, ok := c.Planet(sourcePlanet)
+4
View File
@@ -118,6 +118,10 @@ func buildGameOnMap(races []string, m generator.Map) (*game.Game, error) {
gameMap.Planet[i].Number, gameMap.Planet[j].Number = gameMap.Planet[j].Number, gameMap.Planet[i].Number
})
for i := range gameMap.Planet {
g.Votes = g.Votes.Add(gameMap.Planet[i].Votes())
}
g.Map = *gameMap
return g, nil
+10
View File
@@ -13,6 +13,9 @@ func MakeTurn(c *Controller, r Repo) error {
// Next turn
c.Cache.g.Turn += 1
// 00. Вышедшие расы удаляются из списка участвующих рас перед началом просчета очередного хода
c.Cache.TurnWipeExtinctRaces()
// 01. Корабли, где это возможно, объединяются в группы.
c.Cache.TurnMergeEqualShipGroups()
@@ -118,6 +121,13 @@ func MakeTurn(c *Controller, r Repo) error {
}
}
for i := range c.Cache.g.Race {
if c.Cache.g.Race[i].Extinct {
continue
}
c.Cache.g.Race[i].TTL -= 1
}
// [ ] monitor memory consumption at this point?
return nil
}
-38
View File
@@ -4,7 +4,6 @@ import (
"fmt"
"iter"
"slices"
"strings"
"github.com/google/uuid"
e "github.com/iliadenisov/galaxy/internal/error"
@@ -12,14 +11,6 @@ import (
"github.com/iliadenisov/galaxy/internal/util"
)
func (c *Controller) RenamePlanet(raceName string, planetNumber int, typeName string) error {
ri, err := c.Cache.raceIndex(raceName)
if err != nil {
return err
}
return c.Cache.RenamePlanet(ri, planetNumber, typeName)
}
func (c *Cache) RenamePlanet(ri int, number int, name string) error {
n, ok := util.ValidateTypeName(name)
if !ok {
@@ -39,35 +30,6 @@ func (c *Cache) RenamePlanet(ri int, number int, name string) error {
return nil
}
func (c *Controller) PlanetProduction(raceName string, planetNumber int, prodType, subject string) error {
ri, err := c.Cache.raceIndex(raceName)
if err != nil {
return err
}
var prod game.ProductionType
switch game.ProductionType(strings.ToUpper(prodType)) {
case game.ProductionMaterial:
prod = game.ProductionMaterial
case game.ProductionCapital:
prod = game.ProductionCapital
case game.ResearchDrive:
prod = game.ResearchDrive
case game.ResearchWeapons:
prod = game.ResearchWeapons
case game.ResearchShields:
prod = game.ResearchShields
case game.ResearchCargo:
prod = game.ResearchCargo
case game.ResearchScience:
prod = game.ResearchScience
case game.ProductionShip:
prod = game.ProductionShip
default:
return e.NewProductionInvalidError(prodType)
}
return c.Cache.PlanetProduction(ri, planetNumber, prod, subject)
}
func (c *Cache) PlanetProduction(ri int, number int, prod game.ProductionType, subj string) error {
c.validateRaceIndex(ri)
if number < 0 {
+76 -44
View File
@@ -2,6 +2,7 @@ package controller
import (
"fmt"
"iter"
"slices"
e "github.com/iliadenisov/galaxy/internal/error"
@@ -9,40 +10,11 @@ import (
"github.com/iliadenisov/galaxy/internal/model/game"
)
func (c Controller) Relation(from, to string) (game.Relation, error) {
r1, err := c.Cache.raceIndex(from)
if err != nil {
return game.Relation(""), err
}
r2, err := c.Cache.raceIndex(to)
if err != nil {
return game.Relation(""), err
}
return c.Cache.Relation(r1, r2), nil
}
func (c Controller) UpdateRelation(race, opponent string, rel game.Relation) error {
ri, err := c.Cache.raceIndex(race)
if err != nil {
return err
}
var other int
if race == opponent {
other = ri
} else if other, err = c.Cache.raceIndex(opponent); err != nil {
return err
}
if err != nil {
return err
}
return c.Cache.UpdateRelation(ri, other, rel)
}
func (c *Cache) Relation(r1, r2 int) game.Relation {
if c.cacheRelation == nil {
c.cacheRelation = make(map[int]map[int]game.Relation)
for r1 := range c.g.Race {
for r2 := range c.g.Race {
for r1 := range c.listRaceActingIdx() {
for r2 := range c.listRaceActingIdx() {
if r1 == r2 {
continue
}
@@ -78,19 +50,6 @@ func (c *Cache) updateRelationCache(r1, r2 int, rel game.Relation) {
c.cacheRelation[r1][r2] = rel
}
func (c *Cache) GiveVotes(race, recipient string) error {
ri, err := c.raceIndex(race)
if err != nil {
return err
}
rec, err := c.raceIndex(recipient)
if err != nil {
return err
}
c.g.Race[ri].VoteFor = c.g.Race[rec].ID
return nil
}
func (c *Cache) Voted(ri int) int {
c.validateRaceIndex(ri)
return c.RaceIndex(c.g.Race[ri].VoteFor)
@@ -123,6 +82,26 @@ func (c *Cache) validateRaceIndex(i int) {
}
}
func (c *Cache) validActor(name string) (int, error) {
i, err := c.validRace(name)
if err != nil {
return -1, err
}
c.g.Race[i].TTL = 10
return i, nil
}
func (c *Cache) validRace(name string) (int, error) {
i, err := c.raceIndex(name)
if err != nil {
return -1, err
}
if c.g.Race[i].Extinct {
return -1, e.NewRaceExinctError(name)
}
return i, nil
}
func (c *Cache) raceIndex(name string) (int, error) {
i := slices.IndexFunc(c.g.Race, func(r game.Race) bool { return r.Name == name })
if i < 0 {
@@ -135,3 +114,56 @@ func (c *Cache) raceTechLevel(ri int, t game.Tech, v float64) {
c.validateRaceIndex(ri)
c.g.Race[ri].Tech = c.g.Race[ri].Tech.Set(t, v)
}
func (c *Cache) TurnWipeExtinctRaces() {
for i := range c.listRaceActingIdx() {
if c.g.Race[i].TTL == 0 {
c.wipeRace(i)
}
}
}
func (c *Cache) wipeRace(ri int) {
c.validateRaceIndex(ri)
r := &c.g.Race[ri]
c.g.ShipGroups = slices.DeleteFunc(c.g.ShipGroups, func(v game.ShipGroup) bool { return v.OwnerID == r.ID })
c.g.Fleets = slices.DeleteFunc(c.g.Fleets, func(v game.Fleet) bool { return v.OwnerID == r.ID })
clear(r.ShipTypes)
clear(r.Sciences)
for i := range c.g.Map.Planet {
p := &c.g.Map.Planet[i]
if p.Owner != nil && *p.Owner != r.ID {
continue
}
p.Wipe()
}
r.Votes = 0
r.VoteFor = r.ID
r.Extinct = true
r.TTL = 0
c.invalidateFleetCache()
c.invalidateShipGroupCache()
}
func (c *Cache) listRaceActingIdx() iter.Seq[int] {
return func(yield func(int) bool) {
for i := range c.listRaceIdx() {
if c.g.Race[i].Extinct {
continue
}
if !yield(i) {
return
}
}
}
}
func (c *Cache) listRaceIdx() iter.Seq[int] {
return func(yield func(int) bool) {
for i := range c.g.Race {
if !yield(i) {
return
}
}
}
}
+4 -4
View File
@@ -8,15 +8,15 @@ import (
)
func TestGiveVotes(t *testing.T) {
c, _ := newCache()
c, g := newCache()
assert.Equal(t, c.Voted(Race_0_idx), Race_0_idx)
assert.Equal(t, c.Voted(Race_1_idx), Race_1_idx)
assert.NoError(t, c.GiveVotes(Race_0.Name, Race_1.Name))
assert.NoError(t, g.GiveVotes(Race_0.Name, Race_1.Name))
assert.Equal(t, Race_1_idx, c.Voted(Race_0_idx))
assert.Equal(t, Race_1_idx, c.Voted(Race_1_idx))
assert.ErrorContains(t, c.GiveVotes("UnknownRace", Race_1.Name), e.GenericErrorText(e.ErrInputUnknownRace))
assert.ErrorContains(t, c.GiveVotes(Race_0.Name, "UnknownRace"), e.GenericErrorText(e.ErrInputUnknownRace))
assert.ErrorContains(t, g.GiveVotes("UnknownRace", Race_1.Name), e.GenericErrorText(e.ErrInputUnknownRace))
assert.ErrorContains(t, g.GiveVotes(Race_0.Name, "UnknownRace"), e.GenericErrorText(e.ErrInputUnknownRace))
}
+3 -2
View File
@@ -15,7 +15,7 @@ import (
func (c *Cache) Report(t uint, battles []*mr.BattleReport, bombings []*mr.Bombing) iter.Seq[*mr.Report] {
report := c.InitReport(t)
return func(yield func(*mr.Report) bool) {
for i := range c.g.Race {
for i := range c.listRaceActingIdx() {
c.ReportRace(i, report, battles, bombings)
if !yield(report) {
break
@@ -55,12 +55,13 @@ func (c *Cache) InitReport(t uint) *mr.Report {
planets[ri] = planets[ri] + 1
}
for ri := range c.g.Race {
for ri := range c.listRaceIdx() {
r := &c.g.Race[ri]
rr := &report.Player[ri]
rr.ID = r.ID
rr.Name = r.Name
rr.Extinct = r.Extinct
rr.Drive = mr.F(r.TechLevel(game.TechDrive))
rr.Weapons = mr.F(r.TechLevel(game.TechWeapons))
rr.Shields = mr.F(r.TechLevel(game.TechShields))
+17 -41
View File
@@ -13,18 +13,6 @@ import (
"github.com/iliadenisov/galaxy/internal/util"
)
func (c *Controller) SetRoute(raceName, loadType string, origin, destination uint) error {
ri, err := c.Cache.raceIndex(raceName)
if err != nil {
return err
}
rt, ok := game.RouteTypeSet[loadType]
if !ok {
return e.NewCargoTypeInvalidError(loadType)
}
return c.Cache.SetRoute(ri, rt, origin, destination)
}
func (c *Cache) SetRoute(ri int, rt game.RouteType, origin, destination uint) error {
c.validateRaceIndex(ri)
p1, ok := c.Planet(origin)
@@ -48,18 +36,6 @@ func (c *Cache) SetRoute(ri int, rt game.RouteType, origin, destination uint) er
return nil
}
func (c *Controller) RemoveRoute(raceName, loadType string, origin uint) error {
ri, err := c.Cache.raceIndex(raceName)
if err != nil {
return err
}
rt, ok := game.RouteTypeSet[loadType]
if !ok {
return e.NewCargoTypeInvalidError(loadType)
}
return c.Cache.RemoveRoute(ri, rt, origin)
}
func (c *Cache) RemoveRoute(ri int, rt game.RouteType, origin uint) error {
c.validateRaceIndex(ri)
p1, ok := c.Planet(origin)
@@ -269,23 +245,6 @@ func (c *Cache) selectColUnloadGroup(groups []int) (result iter.Seq[int]) {
return
}
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)
@@ -311,3 +270,20 @@ func (c *Cache) listRoutedUnloadShipGroupIds(pn uint, routeType game.RouteType)
}
}
}
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]
}
+1 -1
View File
@@ -115,7 +115,7 @@ func TestListRoutedSendGroupIds(t *testing.T) {
// Foreign group -> idx 1
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 10))
assert.NoError(t, g.GiveawayGroup(Race_0.Name, Race_1.Name, 5, 0))
assert.NoError(t, g.TransferGroup(Race_0.Name, Race_1.Name, 5, 0))
// 5: idx = 4 / Part of the Fleet
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 10))
-16
View File
@@ -10,14 +10,6 @@ import (
"github.com/iliadenisov/galaxy/internal/util"
)
func (c *Controller) CreateScience(raceName, typeName string, drive, weapons, shields, cargo float64) error {
ri, err := c.Cache.raceIndex(raceName)
if err != nil {
return err
}
return c.Cache.CreateScience(ri, typeName, drive, weapons, shields, cargo)
}
func (c *Cache) CreateScience(ri int, name string, drive, weapons, shileds, cargo float64) error {
c.validateRaceIndex(ri)
n, ok := util.ValidateTypeName(name)
@@ -55,14 +47,6 @@ func (c *Cache) CreateScience(ri int, name string, drive, weapons, shileds, carg
return nil
}
func (c *Controller) DeleteScience(raceName, typeName string) error {
ri, err := c.Cache.raceIndex(raceName)
if err != nil {
return err
}
return c.Cache.DeleteScience(ri, typeName)
}
func (c *Cache) DeleteScience(ri int, name string) error {
c.validateRaceIndex(ri)
sc := slices.IndexFunc(c.g.Race[ri].Sciences, func(s game.Science) bool { return s.Name == name })
-33
View File
@@ -11,14 +11,6 @@ import (
"github.com/iliadenisov/galaxy/internal/util"
)
func (c *Controller) CreateShipType(raceName, typeName string, drive float64, ammo int, weapons, shileds, cargo float64) error {
ri, err := c.Cache.raceIndex(raceName)
if err != nil {
return err
}
return c.Cache.CreateShipType(ri, typeName, drive, ammo, weapons, shileds, cargo)
}
func (c *Cache) CreateShipType(ri int, typeName string, drive float64, ammo int, weapons, shileds, cargo float64) error {
c.validateRaceIndex(ri)
if err := checkShipTypeValues(drive, ammo, weapons, shileds, cargo); err != nil {
@@ -45,14 +37,6 @@ func (c *Cache) CreateShipType(ri int, typeName string, drive float64, ammo int,
return nil
}
func (c *Controller) MergeShipType(race, name, targetName string) error {
ri, err := c.Cache.raceIndex(race)
if err != nil {
return err
}
return c.Cache.MergeShipType(ri, name, targetName)
}
func (c *Cache) MergeShipType(ri int, name, targetName string) error {
c.validateRaceIndex(ri)
st, sti, ok := c.ShipClass(ri, name)
@@ -101,14 +85,6 @@ func (c *Cache) MergeShipType(ri int, name, targetName string) error {
return nil
}
func (c *Controller) DeleteShipType(raceName, typeName string) error {
ri, err := c.Cache.raceIndex(raceName)
if err != nil {
return err
}
return c.Cache.DeleteShipType(ri, typeName)
}
func (c *Cache) DeleteShipType(ri int, name string) error {
c.validateRaceIndex(ri)
st, i, ok := c.ShipClass(ri, name)
@@ -136,15 +112,6 @@ func (c *Cache) DeleteShipType(ri int, name string) error {
return nil
}
// ShipTypes used for tests only
func (c *Controller) ShipTypes(race string) ([]*game.ShipType, error) {
ri, err := c.Cache.raceIndex(race)
if err != nil {
return nil, err
}
return c.Cache.ShipTypes(ri), nil
}
// ShipTypes used for tests only
func (c *Cache) ShipTypes(ri int) []*game.ShipType {
c.validateRaceIndex(ri)
+46 -75
View File
@@ -118,29 +118,39 @@ func (c *Cache) DeleteShipGroup(i int) {
}
func (c *Cache) DeleteKilledShipGroups() {
keepFleet := make(map[uuid.UUID]bool, len(c.g.Fleets))
for i := len(c.g.ShipGroups) - 1; i >= 0; i-- {
if c.g.ShipGroups[i].FleetID != nil {
id := *c.g.ShipGroups[i].FleetID
keepFleet[id] = keepFleet[id] || c.g.ShipGroups[i].Number > 0
}
if c.g.ShipGroups[i].Number == 0 {
c.unsafeDeleteShipGroup(i)
}
}
// TODO: delete empty fleets
}
func (c *Controller) JoinEqualGroups(raceName string) error {
ri, err := c.Cache.raceIndex(raceName)
if err != nil {
return err
for id, keep := range keepFleet {
if keep {
continue
}
c.unsafeDeleteFleet(c.MustFleetIndex(id))
}
c.Cache.JoinEqualGroups(ri)
return nil
}
func (c *Cache) TurnMergeEqualShipGroups() {
for i := range c.g.Race {
for i := range c.listRaceActingIdx() {
c.transferPendingGroups(i)
c.JoinEqualGroups(i)
}
}
func (c *Cache) transferPendingGroups(ri int) {
for sg := range c.listShipGroups(ri) {
if sg.State() == game.StateTransfer {
sg.StateTransfer = false
}
}
}
func (c *Cache) JoinEqualGroups(ri int) {
c.validateRaceIndex(ri)
raceGroups := make([]game.ShipGroup, 0)
@@ -183,30 +193,14 @@ func (c *Cache) JoinEqualGroups(ri int) {
}
}
func (c *Controller) BreakGroup(raceName string, groupIndex, quantity uint) error {
ri, err := c.Cache.raceIndex(raceName)
if err != nil {
return err
}
return c.Cache.BreakGroup(ri, groupIndex, quantity)
}
func (c *Controller) DisassembleGroup(raceName string, groupIndex, quantity uint) error {
ri, err := c.Cache.raceIndex(raceName)
if err != nil {
return err
}
return c.Cache.DisassembleGroup(ri, groupIndex, quantity)
}
func (c *Cache) DisassembleGroup(ri int, groupIndex, quantity uint) error {
sgi, ok := c.raceShipGroupIndex(ri, groupIndex)
if !ok {
return e.NewEntityNotExistsError("group #%d", groupIndex)
}
if c.ShipGroup(sgi).State() != game.StateInOrbit {
return e.NewShipsBusyError()
if state := c.ShipGroup(sgi).State(); state != game.StateInOrbit {
return e.NewShipsBusyError("state: %s", state)
}
if c.ShipGroup(sgi).Number < quantity {
@@ -254,18 +248,6 @@ func (c *Cache) DisassembleGroup(ri int, groupIndex, quantity uint) error {
return nil
}
func (c *Controller) LoadCargo(raceName string, groupIndex uint, cargoType string, ships uint, quantity float64) error {
ri, err := c.Cache.raceIndex(raceName)
if err != nil {
return err
}
ct, ok := game.CargoTypeSet[cargoType]
if !ok {
return e.NewCargoTypeInvalidError(cargoType)
}
return c.Cache.LoadCargo(ri, groupIndex, ct, ships, quantity)
}
// Корабль может нести только один тип груза одновременно.
// Возможные типы груза - это колонисты, сырье и промышленность.
// Груз может быть доставлен на борт корабля с Вашей или не занятой планеты, на которой он имеется.
@@ -277,8 +259,8 @@ func (c *Cache) LoadCargo(ri int, groupIndex uint, ct game.CargoType, ships uint
if !ok {
return e.NewEntityNotExistsError("group #%d", groupIndex)
}
if c.ShipGroup(sgi).State() != game.StateInOrbit {
return e.NewShipsBusyError()
if state := c.ShipGroup(sgi).State(); state != game.StateInOrbit {
return e.NewShipsBusyError("state: %s", state)
}
p, ok := c.Planet(c.ShipGroup(sgi).Destination)
if !ok {
@@ -337,14 +319,6 @@ func (c *Cache) LoadCargo(ri int, groupIndex uint, ct game.CargoType, ships uint
return nil
}
func (c *Controller) UnloadCargo(raceName string, groupIndex uint, ships uint, quantity float64) error {
ri, err := c.Cache.raceIndex(raceName)
if err != nil {
return err
}
return c.Cache.UnloadCargo(ri, groupIndex, ships, quantity)
}
// Промышленность и Сырье могут быть выгружены на любой планете.
// Колонисты могут быть высажены только на планеты, принадлежащие Вам или на необитаемые планеты.
func (c *Cache) UnloadCargo(ri int, groupIndex uint, ships uint, quantity float64) error {
@@ -356,8 +330,8 @@ func (c *Cache) UnloadCargo(ri int, groupIndex uint, ships uint, quantity float6
if !ok {
return e.NewEntityNotExistsError("group #%d", groupIndex)
}
if c.ShipGroup(sgi).State() != game.StateInOrbit {
return e.NewShipsBusyError()
if state := c.ShipGroup(sgi).State(); state != game.StateInOrbit {
return e.NewShipsBusyError("state: %s", state)
}
st := c.ShipGroupShipClass(sgi)
if st.Cargo < 1 {
@@ -429,19 +403,7 @@ func (c *Cache) unsafeUnloadCargo(sgi int, q float64) {
p.UnpackCapital()
}
func (c *Controller) GiveawayGroup(raceName, raceAcceptor string, groupIndex, quantity uint) error {
ri, err := c.Cache.raceIndex(raceName)
if err != nil {
return err
}
riAccept, err := c.Cache.raceIndex(raceAcceptor)
if err != nil {
return err
}
return c.Cache.GiveawayGroup(ri, riAccept, groupIndex, quantity)
}
func (c *Cache) GiveawayGroup(ri, riAccept int, groupIndex, quantity uint) (err error) {
func (c *Cache) TransferGroup(ri, riAccept int, groupIndex, quantity uint) (err error) {
if ri == riAccept {
return e.NewSameRaceError(c.g.Race[riAccept].Name)
}
@@ -449,8 +411,13 @@ func (c *Cache) GiveawayGroup(ri, riAccept int, groupIndex, quantity uint) (err
if !ok {
return e.NewEntityNotExistsError("group #%d", groupIndex)
}
if c.ShipGroup(sgi).Number < quantity {
return e.NewBeakGroupNumberNotEnoughError("%d<%d", c.ShipGroup(sgi).Number, quantity)
sg := c.ShipGroup(sgi)
state := sg.State()
if state == game.StateTransfer {
return e.NewShipsBusyError("state: %s", state)
}
if sg.Number < quantity {
return e.NewBeakGroupNumberNotEnoughError("%d<%d", sg.Number, quantity)
}
st := c.ShipGroupShipClass(sgi)
@@ -474,18 +441,22 @@ func (c *Cache) GiveawayGroup(ri, riAccept int, groupIndex, quantity uint) (err
stAcc = len(c.g.Race[riAccept].ShipTypes) - 1
}
sg := *(c.ShipGroup(sgi))
sg.TypeID = c.g.Race[riAccept].ShipTypes[stAcc].ID
sg.Number = uint(quantity)
sg.Tech = maps.Clone(sg.Tech)
c.appendShipGroup(riAccept, &sg)
newGroup := *(sg)
newGroup.TypeID = c.g.Race[riAccept].ShipTypes[stAcc].ID
newGroup.Tech = maps.Clone(sg.Tech)
if state == game.StateLaunched {
newGroup.StateTransfer = true
}
if quantity == 0 || quantity == c.ShipGroup(sgi).Number {
if quantity == 0 || quantity == sg.Number {
c.unsafeDeleteShipGroup(sgi)
} else {
c.ShipGroup(sgi).Number -= quantity
newGroup.Number = quantity
sg.Number -= quantity
}
c.appendShipGroup(riAccept, &newGroup)
return nil
}
@@ -502,7 +473,7 @@ func (c *Cache) BreakGroup(ri int, groupIndex, quantity uint) error {
return e.NewEntityNotExistsError("group #%d", groupIndex)
}
if c.ShipGroup(sgi).State() != game.StateInOrbit {
if state := c.ShipGroup(sgi).State(); state != game.StateInOrbit {
return e.NewShipsBusyError()
}
+1 -9
View File
@@ -6,14 +6,6 @@ import (
"github.com/iliadenisov/galaxy/internal/util"
)
func (c *Controller) SendGroup(raceName string, groupIndex, planetNumber, quantity uint) error {
ri, err := c.Cache.raceIndex(raceName)
if err != nil {
return err
}
return c.Cache.SendGroup(ri, groupIndex, planetNumber, quantity)
}
func (c *Cache) SendGroup(ri int, groupIndex, planetNumber, quantity uint) error {
c.validateRaceIndex(ri)
@@ -29,7 +21,7 @@ func (c *Cache) SendGroup(ri int, groupIndex, planetNumber, quantity uint) error
sourcePlanet, ok := c.ShipGroup(sgi).AtPlanet()
if !ok {
return e.NewShipsBusyError()
return e.NewShipsBusyError("state: %s", c.ShipGroup(sgi).State())
}
if c.ShipGroup(sgi).Number < quantity {
+31 -9
View File
@@ -1,6 +1,7 @@
package controller_test
import (
"fmt"
"slices"
"testing"
@@ -159,7 +160,7 @@ func TestBreakGroup(t *testing.T) {
assert.Nil(t, c.ShipGroup(3).FleetID)
}
func TestGiveawayGroup(t *testing.T) {
func TestTransferGroup(t *testing.T) {
c, g := newCache()
assert.NoError(t, c.CreateShips(Race_0_idx, ShipType_Cruiser, R0_Planet_0_num, 11)) // group #1 (0)
assert.NoError(t, c.CreateShips(Race_1_idx, ShipType_Cruiser, R1_Planet_1_num, 23)) // group #1 (1)
@@ -178,25 +179,25 @@ func TestGiveawayGroup(t *testing.T) {
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_1_idx)), 1)
assert.ErrorContains(t,
g.GiveawayGroup("UnknownRace", Race_1.Name, 2, 0),
g.TransferGroup("UnknownRace", Race_1.Name, 2, 0),
e.GenericErrorText(e.ErrInputUnknownRace))
assert.ErrorContains(t,
g.GiveawayGroup(Race_0.Name, "UnknownRace", 2, 0),
g.TransferGroup(Race_0.Name, "UnknownRace", 2, 0),
e.GenericErrorText(e.ErrInputUnknownRace))
assert.ErrorContains(t,
g.GiveawayGroup(Race_0.Name, Race_0.Name, 2, 0),
g.TransferGroup(Race_0.Name, Race_0.Name, 2, 0),
e.GenericErrorText(e.ErrInputSameRace))
assert.ErrorContains(t,
g.GiveawayGroup(Race_0.Name, Race_1.Name, 555, 0),
g.TransferGroup(Race_0.Name, Race_1.Name, 555, 0),
e.GenericErrorText(e.ErrInputEntityNotExists))
assert.ErrorContains(t,
g.GiveawayGroup(Race_0.Name, Race_1.Name, 2, 18),
g.TransferGroup(Race_0.Name, Race_1.Name, 2, 18),
e.GenericErrorText(e.ErrBeakGroupNumberNotEnough))
assert.ErrorContains(t,
g.GiveawayGroup(Race_0.Name, Race_1.Name, 1, 0),
g.TransferGroup(Race_0.Name, Race_1.Name, 1, 0),
e.GenericErrorText(e.ErrGiveawayGroupShipsTypeNotEqual))
assert.NoError(t, g.GiveawayGroup(Race_0.Name, Race_1.Name, 2, 11)) // group #2 (3)
assert.NoError(t, g.TransferGroup(Race_0.Name, Race_1.Name, 2, 11)) // group #2 (3)
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 2)
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_1_idx)), 2)
@@ -216,14 +217,31 @@ func TestGiveawayGroup(t *testing.T) {
assert.Equal(t, c.ShipGroup(2).Destination, c.ShipGroup(3).Destination)
assert.Equal(t, c.ShipGroup(2).StateInSpace, c.ShipGroup(3).StateInSpace)
assert.Equal(t, c.ShipGroup(2).StateUpgrade, c.ShipGroup(3).StateUpgrade)
assert.Equal(t, c.ShipGroup(2).StateTransfer, c.ShipGroup(3).StateTransfer)
assert.Equal(t, c.ShipGroup(3).OwnerID, Race_1_ID)
assert.Equal(t, c.ShipGroup(3).TypeID, c.MustShipClass(Race_1_idx, Race_0_Gunship).ID)
assert.Equal(t, c.ShipGroup(3).Number, uint(11))
assert.Nil(t, c.ShipGroup(3).FleetID)
assert.NoError(t, g.GiveawayGroup(Race_1.Name, Race_0.Name, 2, 11))
assert.NoError(t, g.TransferGroup(Race_1.Name, Race_0.Name, 2, 11))
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 3)
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_1_idx)), 1)
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Gunship, R0_Planet_0_num, 1))
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 4)
assert.Equal(t, game.StateInOrbit, c.ShipGroup(4).State())
assert.NoError(t, g.SendGroup(Race_0.Name, c.ShipGroup(4).Index, R0_Planet_2_num, 0))
assert.Equal(t, game.StateLaunched, c.ShipGroup(4).State())
assert.Equal(t, c.ShipGroup(4).OwnerID, Race_0_ID)
assert.NoError(t, g.TransferGroup(Race_0.Name, Race_1.Name, c.ShipGroup(4).Index, 0))
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 3)
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_1_idx)), 2)
assert.Equal(t, game.StateTransfer, c.ShipGroup(4).State())
assert.Equal(t, c.ShipGroup(4).OwnerID, Race_1_ID)
assert.ErrorContains(t,
g.TransferGroup(Race_1.Name, Race_0.Name, c.ShipGroup(4).Index, 0),
e.GenericErrorText(e.ErrShipsBusy))
}
func TestLoadCargo(t *testing.T) {
@@ -540,3 +558,7 @@ func TestShipGroupDestroyItem(t *testing.T) {
assert.Equal(t, float64(c.ShipGroup(0).Number)*10, c.ShipGroup(0).Load.F())
}
}
func TestState(t *testing.T) {
assert.Equal(t, "In_Orbit", fmt.Sprintf("%s", game.StateInOrbit))
}
+2 -10
View File
@@ -9,14 +9,6 @@ import (
"github.com/iliadenisov/galaxy/internal/model/game"
)
func (c *Controller) UpgradeGroup(raceName string, groupIndex uint, techInput string, limitShips uint, limitLevel float64) error {
ri, err := c.Cache.raceIndex(raceName)
if err != nil {
return err
}
return c.Cache.UpgradeGroup(ri, groupIndex, techInput, limitShips, limitLevel)
}
func (c *Cache) UpgradeGroup(ri int, groupIndex uint, techInput string, limitShips uint, limitLevel float64) error {
c.validateRaceIndex(ri)
sgi, ok := c.raceShipGroupIndex(ri, groupIndex)
@@ -26,8 +18,8 @@ func (c *Cache) UpgradeGroup(ri int, groupIndex uint, techInput string, limitShi
st := c.ShipGroupShipClass(sgi)
sg := c.ShipGroup(sgi)
if s := sg.State(); s != game.StateInOrbit { // && s != game.StateUpgrade
return e.NewShipsBusyError()
if state := sg.State(); state != game.StateInOrbit {
return e.NewShipsBusyError("state: %s", state)
}
p := c.MustPlanet(sg.Destination)
+4 -1
View File
@@ -27,6 +27,7 @@ const (
ErrUpgradeInsufficientResources = 5011
ErrSendShipHasNoDrives = 5012
ErrSendUnreachableDestination = 5013
ErrRaceExinct = 5014
)
const (
@@ -141,7 +142,7 @@ func GenericErrorText(code int) string {
case ErrBeakGroupNumberNotEnough:
return "Not enough ships in the group to make a separate group"
case ErrShipsBusy:
return "Ships currently not in orbit or free to use"
return "Ship(s) are'n free to use"
case ErrShipsNotOnSamePlanet:
return "Ships not on the same planet"
case ErrGiveawayGroupShipsTypeNotEqual:
@@ -166,6 +167,8 @@ func GenericErrorText(code int) string {
return "One or more ships are not equipped with hyperdrive and cannot be moved"
case ErrSendUnreachableDestination:
return "Destination planet is too far for current Drive level"
case ErrRaceExinct:
return "Race is extinct"
default:
return fmt.Sprintf("Undescribed error with code %d", code)
}
+4
View File
@@ -1,5 +1,9 @@
package error
func NewRaceExinctError(arg ...any) error {
return newGenericError(ErrRaceExinct, arg...)
}
func NewGameNotInitializedError(arg ...any) error {
return newGenericError(ErrGameNotInitialized, arg...)
}
+4 -7
View File
@@ -22,8 +22,7 @@ func TestCreateShipType(t *testing.T) {
assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputUnknownRace))
err = game.CreateShipType(p, race, " "+typeName+" ", 1, 0, 0, 0, 0)
assert.NoError(t, err)
st, err := ctrl().ShipTypes(race)
assert.NoError(t, err)
st := ctrl().Cache.ShipTypes(1)
assert.Len(t, st, 1)
assert.Equal(t, st[0].Name, typeName)
assert.Equal(t, st[0].Drive.F(), 1.)
@@ -36,8 +35,7 @@ func TestCreateShipType(t *testing.T) {
assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputUnknownRace))
err = game.DeleteShipType(p, race, typeName)
assert.NoError(t, err)
st, err = ctrl().ShipTypes(race)
assert.NoError(t, err)
st = ctrl().Cache.ShipTypes(1)
assert.Len(t, st, 0)
})
}
@@ -96,7 +94,7 @@ func TestCreateShipTypeValidation(t *testing.T) {
func TestMergeShipType(t *testing.T) {
race := "race_01"
c(t, func(p func(*controller.Param), ctl func() *controller.Controller) {
c(t, func(p func(*controller.Param), ctrl func() *controller.Controller) {
err := game.CreateShipType(p, race, "Drone", 1, 0, 0, 0, 0)
assert.NoError(t, err)
err = game.CreateShipType(p, race, "Spy", 1, 0, 0, 0, 0)
@@ -109,8 +107,7 @@ func TestMergeShipType(t *testing.T) {
assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputEntityNotExists))
err = game.MergeShipType(p, race, "Spy", "Drone")
assert.NoError(t, err)
st, err := ctl().ShipTypes(race)
assert.NoError(t, err)
st := ctrl().Cache.ShipTypes(1)
assert.Len(t, st, 2)
err = game.MergeShipType(p, race, "Drone", "Cruiser")
assert.ErrorContains(t, err, e.GenericErrorText(e.ErrMergeShipTypeNotEqual))
+7 -3
View File
@@ -38,7 +38,7 @@ const (
StateLaunched ShipGroupState = "Launched"
StateInSpace ShipGroupState = "In_Space"
StateUpgrade ShipGroupState = "Upgrade"
StateTransfer ShipGroupState = "Transfer_Status"
StateTransfer ShipGroupState = "Transfer"
)
func (sgs ShipGroupState) String() string {
@@ -113,8 +113,9 @@ type ShipGroup struct {
Load Float `json:"load"` // Cargo loaded - "Масса груза"
Tech TechSet `json:"tech"` //
Destination uint `json:"destination"` // TODO: TEST: Destination, Origin, Range
StateInSpace *InSpace `json:"stateInSpace,omitempty"` //
StateUpgrade *InUpgrade `json:"stateUpgrade,omitempty"` //
StateInSpace *InSpace `json:"inSpace,omitempty"` //
StateUpgrade *InUpgrade `json:"upgrade,omitempty"` //
StateTransfer bool `json:"transfer,omitempty"` //
}
func (sg ShipGroup) TechLevel(t Tech) Float {
@@ -134,6 +135,9 @@ func (sg ShipGroup) State() ShipGroupState {
if sg.StateInSpace.Range > 0 {
return StateInSpace
}
if sg.StateTransfer {
return StateTransfer
}
return StateLaunched
case sg.StateUpgrade != nil && sg.StateInSpace == nil:
return StateUpgrade
+7
View File
@@ -38,9 +38,16 @@ func (p *Planet) Free() {
p.Owner = nil
p.Production = ProductionNone.AsType(uuid.Nil)
p.Colonists = 0.
p.Population = 0.
clear(p.Route)
}
func (p *Planet) Wipe() {
p.Free()
p.Industry = 0
p.Capital = 0
}
func (p Planet) Owned() bool {
return p.Owner != nil && *p.Owner != uuid.Nil
}
+1
View File
@@ -5,6 +5,7 @@ import "github.com/google/uuid"
type Race struct {
ID uuid.UUID `json:"id"`
Name string `json:"name"`
TTL uint `json:"ttl"`
Extinct bool `json:"extinct"`
Votes Float `json:"votes"`
VoteFor uuid.UUID `json:"voteFor"`
+1
View File
@@ -63,6 +63,7 @@ type Player struct {
Planets uint16 `json:"planets"`
Relation string `json:"relation"`
Votes Float `json:"votes"`
Extinct bool `json:"extinct"`
}
func (r Report) MarshalBinary() (data []byte, err error) {