From a567229f8ad74442c612c2aff29bf30358521d01 Mon Sep 17 00:00:00 2001 From: Ilia Denisov Date: Mon, 2 Feb 2026 13:14:57 +0200 Subject: [PATCH] refactor: float64 types for storage and report --- internal/controller/battle.go | 4 +- internal/controller/battle_transform.go | 10 +-- internal/controller/bombing.go | 34 ++++----- internal/controller/bombing_test.go | 34 ++++----- internal/controller/fleet_send.go | 2 +- internal/controller/generate_game.go | 12 ++-- internal/controller/planet.go | 14 ++-- internal/controller/planet_test.go | 18 ++--- internal/controller/route.go | 16 ++--- internal/controller/route_test.go | 36 +++++----- internal/controller/ship_group.go | 34 ++++----- internal/controller/ship_group_move.go | 2 +- internal/controller/ship_group_send.go | 4 +- internal/controller/ship_group_send_test.go | 8 +-- internal/controller/ship_group_test.go | 76 ++++++++++----------- internal/controller/ship_group_upgrade.go | 2 +- internal/model/game/group.go | 26 +++---- internal/model/game/group_test.go | 10 +-- internal/model/game/group_upgrade.go | 12 ++-- internal/model/game/planet.go | 62 +++++++++++------ internal/model/game/planet_test.go | 70 +++++++++---------- internal/model/report/battle.go | 10 +-- internal/model/report/bombing.go | 12 ++-- internal/repo/game.go | 9 --- 24 files changed, 264 insertions(+), 253 deletions(-) diff --git a/internal/controller/battle.go b/internal/controller/battle.go index ce8b27f..f0027c4 100644 --- a/internal/controller/battle.go +++ b/internal/controller/battle.go @@ -65,9 +65,9 @@ func FilterBattleOpponents(c *Cache, attIdx, defIdx int, cacheProbability map[in p := DestructionProbability( c.ShipGroupShipClass(attIdx).Weapons, - c.ShipGroup(attIdx).TechLevel(game.TechWeapons), + c.ShipGroup(attIdx).TechLevel(game.TechWeapons).F(), c.ShipGroupShipClass(defIdx).Shields, - c.ShipGroup(defIdx).TechLevel(game.TechShields), + c.ShipGroup(defIdx).TechLevel(game.TechShields).F(), c.ShipGroup(defIdx).FullMass(c.ShipGroupShipClass(defIdx)), ) // Exclude opponent's group which cannot be probably destroyed diff --git a/internal/controller/battle_transform.go b/internal/controller/battle_transform.go index f222330..9e747c7 100644 --- a/internal/controller/battle_transform.go +++ b/internal/controller/battle_transform.go @@ -30,11 +30,11 @@ func TransformBattle(c *Cache, b *Battle) *report.BattleReport { NumberLeft: sg.Number, ClassName: shipClass.Name, LoadType: sg.CargoString(), - LoadQuantity: sg.Load, - Drive: sg.TechLevel(game.TechDrive), - Weapons: sg.TechLevel(game.TechWeapons), - Shields: sg.TechLevel(game.TechShields), - Cargo: sg.TechLevel(game.TechCargo), + LoadQuantity: sg.Load.RF(), + Drive: sg.TechLevel(game.TechDrive).RF(), + Weapons: sg.TechLevel(game.TechWeapons).RF(), + Shields: sg.TechLevel(game.TechShields).RF(), + Cargo: sg.TechLevel(game.TechCargo).RF(), } cacheShipClass[shipClass.ID] = itemNumber return itemNumber diff --git a/internal/controller/bombing.go b/internal/controller/bombing.go index b20d1fe..bff5e58 100644 --- a/internal/controller/bombing.go +++ b/internal/controller/bombing.go @@ -20,12 +20,12 @@ func (c *Cache) bombingReport(p *game.Planet, ri int, groups []int) report.Bombi Owner: c.g.Race[c.RaceIndex(p.Owner)].Name, Attacker: c.g.Race[ri].Name, Production: c.PlanetProductionDisplayName(p.Number), - Industry: p.Industry, - Population: p.Population, - Colonists: p.Colonists, - Capital: p.Capital, - Material: p.Material, - AttackPower: attackPower, + Industry: p.Industry.RF(), + Population: p.Population.RF(), + Colonists: p.Colonists.RF(), + Capital: p.Capital.RF(), + Material: p.Material.RF(), + AttackPower: game.RF(attackPower), } bombPlanet(p, attackPower) r.Wiped = p.Population == 0 @@ -61,23 +61,23 @@ func (c *Cache) ProduceBombings() []report.BombingPlanetReport { func bombPlanet(p *game.Planet, power float64) { // Уничтожается население и колонисты в количестве равном [суммарной] мощности бомбардировки - if power > p.Population { - p.Population = 0 + if power > p.Population.F() { + p.Pop(0) } else { - p.Population -= power + p.Pop(p.Population.F() - power) } - if power > p.Colonists { - p.Colonists = 0 + if power > p.Colonists.F() { + p.Col(0) } else { - p.Colonists -= power + p.Col(p.Colonists.F() - power) } // Такое же количество промышленности превращается в сырье - if power > p.Industry { - p.Material += p.Industry - p.Industry = 0 + if power > p.Industry.F() { + p.Mat(p.Material.F() + p.Industry.F()) + p.Ind(0) } else { - p.Material += power - p.Industry -= power + p.Mat(p.Material.F() + power) + p.Ind(p.Industry.F() - power) } } diff --git a/internal/controller/bombing_test.go b/internal/controller/bombing_test.go index fe85f0b..c172677 100644 --- a/internal/controller/bombing_test.go +++ b/internal/controller/bombing_test.go @@ -12,25 +12,25 @@ import ( func TestBombPlanet(t *testing.T) { p := controller.NewPlanet(0, "Planet_0", uuid.New(), 1, 1, 1000, 300, 200, 10, game.ResearchDrive.AsType(uuid.Nil)) (&p).Colonists = 100. - assert.Equal(t, 0., p.Material) + assert.Equal(t, 0., p.Material.F()) controller.BombPlanet(&p, 55.) - assert.Equal(t, 245., p.Population) - assert.Equal(t, 45., p.Colonists) - assert.Equal(t, 145., p.Industry) - assert.Equal(t, 55., p.Material) + assert.Equal(t, 245., p.Population.F()) + assert.Equal(t, 45., p.Colonists.F()) + assert.Equal(t, 145., p.Industry.F()) + assert.Equal(t, 55., p.Material.F()) controller.BombPlanet(&p, 56.) - assert.Equal(t, 189., p.Population) - assert.Equal(t, 0., p.Colonists) - assert.Equal(t, 89., p.Industry) - assert.Equal(t, 111., p.Material) + assert.Equal(t, 189., p.Population.F()) + assert.Equal(t, 0., p.Colonists.F()) + assert.Equal(t, 89., p.Industry.F()) + assert.Equal(t, 111., p.Material.F()) controller.BombPlanet(&p, 200.) - assert.Equal(t, 0., p.Population) - assert.Equal(t, 0., p.Colonists) - assert.Equal(t, 0., p.Industry) - assert.Equal(t, 200., p.Material) + assert.Equal(t, 0., p.Population.F()) + assert.Equal(t, 0., p.Colonists.F()) + assert.Equal(t, 0., p.Industry.F()) + assert.Equal(t, 200., p.Material.F()) } func TestCollectBombingGroups(t *testing.T) { @@ -126,19 +126,19 @@ func TestProduceBombings(t *testing.T) { case R1_Planet_1_num: assert.Equal(t, Race_1.Name, r.Owner) assert.Equal(t, Race_0.Name, r.Attacker) - assert.InDelta(t, 697.857, r.AttackPower, 0.0003) + assert.InDelta(t, 697.857, r.AttackPower.F(), 0.0003) assert.True(t, r.Wiped) assert.Equal(t, uuid.Nil, c.MustPlanet(pn).Owner) assert.Empty(t, c.MustPlanet(pn).Route) - assert.Equal(t, 0., c.MustPlanet(pn).Population) + assert.Equal(t, 0., c.MustPlanet(pn).Population.F()) case R0_Planet_2_num: assert.Equal(t, Race_0.Name, r.Owner) assert.Equal(t, Race_1.Name, r.Attacker) - assert.InDelta(t, 358.85596, r.AttackPower, 0.000001) + assert.Equal(t, 358.856, r.AttackPower.F()) assert.False(t, r.Wiped) assert.Equal(t, Race_0_ID, c.MustPlanet(pn).Owner) assert.NotEmpty(t, c.MustPlanet(pn).Route) - assert.InDelta(t, 500.-358.85596, c.MustPlanet(pn).Population, 0.000001) + assert.InDelta(t, 500.-358.85596, c.MustPlanet(pn).Population.F(), 0.000001) } } } diff --git a/internal/controller/fleet_send.go b/internal/controller/fleet_send.go index 1b8436b..7b1d2e0 100644 --- a/internal/controller/fleet_send.go +++ b/internal/controller/fleet_send.go @@ -34,7 +34,7 @@ func (c *Cache) SendFleet(ri, fi int, planetNumber uint) error { if !ok { return e.NewEntityNotExistsError("destination planet #%d", planetNumber) } - rangeToDestination := util.ShortDistance(c.g.Map.Width, c.g.Map.Height, p1.X, p1.Y, p2.X, p2.Y) + rangeToDestination := util.ShortDistance(c.g.Map.Width, c.g.Map.Height, p1.X.F(), p1.Y.F(), p2.X.F(), p2.Y.F()) if rangeToDestination > c.g.Race[ri].FlightDistance() { return e.NewSendUnreachableDestinationError("range=%.03f", rangeToDestination) } diff --git a/internal/controller/generate_game.go b/internal/controller/generate_game.go index f380716..837b1e3 100644 --- a/internal/controller/generate_game.go +++ b/internal/controller/generate_game.go @@ -134,16 +134,16 @@ func NewPlanet(num uint, name string, owner uuid.UUID, x, y, size, pop, ind, res PlanetReport: game.PlanetReport{ UninhabitedPlanet: game.UninhabitedPlanet{ UnidentifiedPlanet: game.UnidentifiedPlanet{ - X: x, - Y: y, + X: game.F(x), + Y: game.F(y), Number: num, }, - Size: size, + Size: game.F(size), Name: name, - Resources: res, + Resources: game.F(res), }, - Population: pop, - Industry: ind, + Population: game.F(pop), + Industry: game.F(ind), Production: prod, }, } diff --git a/internal/controller/planet.go b/internal/controller/planet.go index c681807..6902018 100644 --- a/internal/controller/planet.go +++ b/internal/controller/planet.go @@ -108,7 +108,7 @@ func (c *Cache) PlanetProduction(ri int, number int, prod game.ProductionType, s if p.Production.Type == game.ProductionShip && (prod != game.ProductionShip || *subjectID != *p.Production.SubjectID) { mat, _ := c.MustShipType(ri, *p.Production.SubjectID).ProductionCost() - p.Material += mat * (*p.Production.Progress) + p.Mat(p.Material.F() + mat*(*p.Production.Progress)) *p.Production.Progress = 0. } else if prod == game.ProductionShip { // new ship class to produce; otherwise we must have been returned from the func earlier @@ -265,15 +265,15 @@ func (c *Cache) listProducingPlanets() iter.Seq[uint] { // Internal funcs func (c *Cache) putPopulation(pn uint, v float64) { - c.MustPlanet(pn).Population = v + c.MustPlanet(pn).Pop(v) } func (c *Cache) putColonists(pn uint, v float64) { - c.MustPlanet(pn).Colonists = v + c.MustPlanet(pn).Col(v) } func (c *Cache) putMaterial(pn uint, v float64) { - c.MustPlanet(pn).Material = v + c.MustPlanet(pn).Mat(v) } func ProduceShip(p *game.Planet, shipMass float64) uint { @@ -281,20 +281,20 @@ func ProduceShip(p *game.Planet, shipMass float64) uint { if productionAvailable <= 0 { return 0 } - CAP_perShip := shipMass / p.Resources + CAP_perShip := shipMass / p.Resources.F() productionForMass := shipMass * 10. ships := uint(0) flZero := 0. p.Production.Progress = &flZero for productionAvailable > 0 { var productionExtraCAP float64 - if CAP_deficit := p.Capital - CAP_perShip; CAP_deficit < 0 { + if CAP_deficit := p.Capital.F() - CAP_perShip; CAP_deficit < 0 { productionExtraCAP = -CAP_deficit } productionCost := productionExtraCAP + productionForMass if productionAvailable >= productionCost { productionAvailable -= productionCost - p.Capital = p.Capital - (CAP_perShip - productionExtraCAP) + p.Cap(p.Capital.F() - (CAP_perShip - productionExtraCAP)) ships++ } else { progress := productionAvailable / productionCost diff --git a/internal/controller/planet_test.go b/internal/controller/planet_test.go index d1d0abc..6f5910c 100644 --- a/internal/controller/planet_test.go +++ b/internal/controller/planet_test.go @@ -152,7 +152,7 @@ func TestProduceShips(t *testing.T) { assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 1) assert.Equal(t, uint(7), c.ShipGroup(0).Number) assert.NoError(t, g.PlanetProduction(Race_0.Name, pn, "SHIP", "Drone")) - assert.InDelta(t, shipMass*progress, c.MustPlanet(R0_Planet_0_num).Material, 0.00001) // 99.(0099) material build + assert.InDelta(t, shipMass*progress, c.MustPlanet(R0_Planet_0_num).Material.F(), 0.00001) // 99.(0099) material build c.MustPlanet(R0_Planet_0_num).Material = 0 c.MustPlanet(R0_Planet_0_num).Colonists = 0 @@ -197,7 +197,7 @@ func TestProduceShip(t *testing.T) { r = controller.ProduceShip(&p, Drone.EmptyMass()) assert.Equal(t, uint(100), r) assert.Equal(t, 0., *p.Production.Progress) - assert.Equal(t, 0., number.Fixed3(p.Capital)) // TODO: zero CAP is not actual '0.0' after series of decrements + assert.Equal(t, 0., number.Fixed3(p.Capital.F())) // TODO: zero CAP is not actual '0.0' after series of decrements (&p).Production = game.ProductionShip.AsType(uuid.Nil) (&p).Capital = 0. @@ -242,17 +242,17 @@ func TestTurnPlanetProductions(t *testing.T) { pn := int(R0_Planet_0_num) assert.NoError(t, g.PlanetProduction(Race_0.Name, pn, "CAP", "")) - assert.Equal(t, 0.0, c.MustPlanet(R0_Planet_0_num).Capital) - assert.Equal(t, 0.0, c.MustPlanet(R0_Planet_0_num).Colonists) + assert.Equal(t, 0.0, c.MustPlanet(R0_Planet_0_num).Capital.F()) + assert.Equal(t, 0.0, c.MustPlanet(R0_Planet_0_num).Colonists.F()) c.TurnPlanetProductions() - assert.InDelta(t, 196., c.MustPlanet(R0_Planet_0_num).Capital, 0.1) - assert.InDelta(t, 10.0, c.MustPlanet(R0_Planet_0_num).Colonists, 0.000001) // FIXME: should store more exact value + assert.InDelta(t, 196., c.MustPlanet(R0_Planet_0_num).Capital.F(), 0.1) + assert.InDelta(t, 10.0, c.MustPlanet(R0_Planet_0_num).Colonists.F(), 0.000001) // FIXME: should store more exact value assert.NoError(t, g.PlanetProduction(Race_0.Name, pn, "MAT", "")) - assert.Equal(t, 0.0, c.MustPlanet(R0_Planet_0_num).Material) + assert.Equal(t, 0.0, c.MustPlanet(R0_Planet_0_num).Material.F()) c.TurnPlanetProductions() - assert.Equal(t, 10000., c.MustPlanet(R0_Planet_0_num).Material) - assert.InDelta(t, 20.0, c.MustPlanet(R0_Planet_0_num).Colonists, 0.000001) + assert.Equal(t, 10000., c.MustPlanet(R0_Planet_0_num).Material.F()) + assert.InDelta(t, 20.0, c.MustPlanet(R0_Planet_0_num).Colonists.F(), 0.000001) assert.NoError(t, g.PlanetProduction(Race_0.Name, pn, "DRIVE", "")) assert.Equal(t, 1.1, c.Race(Race_0_idx).TechLevel(game.TechDrive)) diff --git a/internal/controller/route.go b/internal/controller/route.go index 898d69b..9186858 100644 --- a/internal/controller/route.go +++ b/internal/controller/route.go @@ -39,7 +39,7 @@ func (c *Cache) SetRoute(ri int, rt game.RouteType, origin, destination uint) er if !ok { return e.NewEntityNotExistsError("destination planet #%d", destination) } - rangeToDestination := util.ShortDistance(c.g.Map.Width, c.g.Map.Height, p1.X, p1.Y, p2.X, p2.Y) + rangeToDestination := util.ShortDistance(c.g.Map.Width, c.g.Map.Height, p1.X.F(), p1.Y.F(), p2.X.F(), p2.Y.F()) if rangeToDestination > c.g.Race[ri].FlightDistance() { return e.NewSendUnreachableDestinationError("range=%.03f max=%.03f", rangeToDestination, c.g.Race[ri].FlightDistance()) } @@ -127,7 +127,7 @@ func (c *Cache) SendRoutedGroups() { if !ok { continue } - var res *float64 + var res *game.Float var ct game.CargoType switch rt { case game.RouteColonist: @@ -152,7 +152,7 @@ func (c *Cache) SendRoutedGroups() { st := c.ShipGroupShipClass(sgi) ships := sg.Number sgCapacity := sg.CargoCapacity(st) - toLoad := *res + toLoad := (*res).F() if toLoad > sgCapacity { toLoad = sgCapacity } else if maxShips := uint(math.Ceil(toLoad / (sgCapacity / float64(ships)))); maxShips < ships { @@ -160,9 +160,9 @@ func (c *Cache) SendRoutedGroups() { sg = c.ShipGroup(newGroupIdx) } // decrease planet resource - *res = *res - toLoad + *res = (*res).Add(-toLoad) // load group - sg.Load += toLoad + sg.Load = sg.Load.Add(toLoad) sg.CargoType = &ct c.LaunchShips(sg, dest) groups = reorderGroups(groups) @@ -214,7 +214,7 @@ func (c *Cache) RemoveUnreachableRoutes() { ri := c.RaceIndex(p1.Owner) for rt, destination := range p1.Route { p2 := c.MustPlanet(destination) - rangeToDestination := util.ShortDistance(c.g.Map.Width, c.g.Map.Height, p1.X, p1.Y, p2.X, p2.Y) + rangeToDestination := util.ShortDistance(c.g.Map.Width, c.g.Map.Height, p1.X.F(), p1.Y.F(), p2.X.F(), p2.Y.F()) if rangeToDestination > c.g.Race[ri].FlightDistance() { delete(p1.Route, rt) } @@ -224,7 +224,7 @@ func (c *Cache) RemoveUnreachableRoutes() { func (c *Cache) doUnload(groups iter.Seq[int]) { for sgi := range groups { - c.unsafeUnloadCargo(sgi, c.ShipGroup(sgi).Load) + c.unsafeUnloadCargo(sgi, c.ShipGroup(sgi).Load.F()) } } @@ -255,7 +255,7 @@ func (c *Cache) selectColUnloadGroup(groups []int) (result iter.Seq[int]) { sg := c.ShipGroup(i) ri := c.RaceIndex(sg.OwnerID) groupByRace[ri] = append(groupByRace[ri], i) - loadByRace[ri] += sg.Load + loadByRace[ri] += sg.Load.F() } if len(loadByRace) < 2 { // only one race has to unload cargo diff --git a/internal/controller/route_test.go b/internal/controller/route_test.go index cf35cb3..cc2e090 100644 --- a/internal/controller/route_test.go +++ b/internal/controller/route_test.go @@ -129,7 +129,7 @@ func TestListRoutedSendGroupIds(t *testing.T) { assert.Equal(t, Race_0_ID, sg.OwnerID) assert.Greater(t, sg.CargoCapacity(st), 0.) assert.Equal(t, game.StateInOrbit, sg.State()) - assert.Equal(t, 0., sg.Load) + assert.Equal(t, 0., sg.Load.F()) assert.Nil(t, sg.FleetID) } } @@ -149,11 +149,11 @@ func TestEnrouteGroups_SplitGroup(t *testing.T) { assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 2) assert.Equal(t, game.StateInOrbit, c.ShipGroup(0).State()) assert.Equal(t, uint(1), c.ShipGroup(0).Number) - assert.Equal(t, 0., c.ShipGroup(0).Load) + assert.Equal(t, 0., c.ShipGroup(0).Load.F()) assert.Equal(t, game.StateLaunched, c.ShipGroup(1).State()) assert.Equal(t, uint(4), c.ShipGroup(1).Number) - assert.Equal(t, 65., c.ShipGroup(1).Load) - assert.Equal(t, 0., c.MustPlanet(R0_Planet_0_num).Colonists) + assert.Equal(t, 65., c.ShipGroup(1).Load.F()) + assert.Equal(t, 0., c.MustPlanet(R0_Planet_0_num).Colonists.F()) } func TestEnrouteGroups_GroupSorting(t *testing.T) { @@ -177,8 +177,8 @@ func TestEnrouteGroups_GroupSorting(t *testing.T) { assert.Equal(t, game.StateInOrbit, c.ShipGroup(0).State()) assert.Equal(t, game.StateLaunched, c.ShipGroup(1).State()) - assert.Equal(t, 100., c.ShipGroup(1).Load) - assert.Equal(t, 0., c.MustPlanet(R0_Planet_0_num).Colonists) + assert.Equal(t, 100., c.ShipGroup(1).Load.F()) + assert.Equal(t, 0., c.MustPlanet(R0_Planet_0_num).Colonists.F()) } func TestEnrouteGroups_LaunchOrder(t *testing.T) { @@ -215,7 +215,7 @@ func TestEnrouteGroups_LaunchOrder(t *testing.T) { sgi := 0 assert.Equal(t, game.StateLaunched, c.ShipGroup(sgi).State()) assert.Equal(t, R0_Planet_2_num, c.ShipGroup(sgi).Destination) - assert.Equal(t, 105., c.ShipGroup(sgi).Load) + assert.Equal(t, 105., c.ShipGroup(sgi).Load.F()) assert.NotNil(t, c.ShipGroup(sgi).CargoType) assert.Equal(t, game.CargoColonist, *c.ShipGroup(sgi).CargoType) assert.Equal(t, uint(5), c.ShipGroup(sgi).Number) @@ -224,7 +224,7 @@ func TestEnrouteGroups_LaunchOrder(t *testing.T) { sgi = 3 assert.Equal(t, game.StateLaunched, c.ShipGroup(sgi).State()) assert.Equal(t, R0_Planet_2_num, c.ShipGroup(sgi).Destination) - assert.Equal(t, 45., c.ShipGroup(sgi).Load) + assert.Equal(t, 45., c.ShipGroup(sgi).Load.F()) assert.NotNil(t, c.ShipGroup(sgi).CargoType) assert.Equal(t, game.CargoColonist, *c.ShipGroup(sgi).CargoType) assert.Equal(t, uint(3), c.ShipGroup(sgi).Number) @@ -233,7 +233,7 @@ func TestEnrouteGroups_LaunchOrder(t *testing.T) { sgi = 2 assert.Equal(t, game.StateLaunched, c.ShipGroup(sgi).State()) assert.Equal(t, R0_Planet_2_num, c.ShipGroup(sgi).Destination) - assert.Equal(t, 100., c.ShipGroup(sgi).Load) + assert.Equal(t, 100., c.ShipGroup(sgi).Load.F()) assert.NotNil(t, c.ShipGroup(sgi).CargoType) assert.Equal(t, game.CargoCapital, *c.ShipGroup(sgi).CargoType) assert.Equal(t, uint(5), c.ShipGroup(sgi).Number) @@ -242,7 +242,7 @@ func TestEnrouteGroups_LaunchOrder(t *testing.T) { sgi = 4 assert.Equal(t, game.StateLaunched, c.ShipGroup(sgi).State()) assert.Equal(t, R0_Planet_2_num, c.ShipGroup(sgi).Destination) - assert.Equal(t, 20., c.ShipGroup(sgi).Load) + assert.Equal(t, 20., c.ShipGroup(sgi).Load.F()) assert.NotNil(t, c.ShipGroup(sgi).CargoType) assert.Equal(t, game.CargoMaterial, *c.ShipGroup(sgi).CargoType) assert.Equal(t, uint(1), c.ShipGroup(sgi).Number) @@ -251,7 +251,7 @@ func TestEnrouteGroups_LaunchOrder(t *testing.T) { sgi = 1 assert.Equal(t, game.StateLaunched, c.ShipGroup(sgi).State()) assert.Equal(t, R1_Planet_1_num, c.ShipGroup(sgi).Destination) - assert.Equal(t, 0., c.ShipGroup(sgi).Load) + assert.Equal(t, 0., c.ShipGroup(sgi).Load.F()) assert.Nil(t, c.ShipGroup(sgi).CargoType) assert.Equal(t, uint(1), c.ShipGroup(sgi).Number) } @@ -406,15 +406,15 @@ func TestTurnUnloadEnroutedGroups(t *testing.T) { c.TurnUnloadEnroutedGroups() - assert.Equal(t, 0., c.ShipGroup(0).Load) - assert.Equal(t, 222., c.MustPlanet(R0_Planet_0_num).Material) - assert.Equal(t, 0., c.ShipGroup(1).Load) - assert.Equal(t, 11., c.MustPlanet(R0_Planet_0_num).Capital) - assert.Equal(t, 0., c.ShipGroup(2).Load) - assert.Equal(t, 96.8, c.MustPlanet(Uninhabited_Planet_4_num).Population) + assert.Equal(t, 0., c.ShipGroup(0).Load.F()) + assert.Equal(t, 222., c.MustPlanet(R0_Planet_0_num).Material.F()) + assert.Equal(t, 0., c.ShipGroup(1).Load.F()) + assert.Equal(t, 11., c.MustPlanet(R0_Planet_0_num).Capital.F()) + assert.Equal(t, 0., c.ShipGroup(2).Load.F()) + assert.Equal(t, 96.8, c.MustPlanet(Uninhabited_Planet_4_num).Population.F()) assert.Equal(t, Race_1_ID, c.MustPlanet(Uninhabited_Planet_4_num).Owner) assert.Equal(t, game.ProductionCapital, c.MustPlanet(Uninhabited_Planet_4_num).Production.Type) - assert.Equal(t, 17.3, c.ShipGroup(3).Load) + assert.Equal(t, 17.3, c.ShipGroup(3).Load.F()) } func TestRemoveUnreachableRoutes(t *testing.T) { diff --git a/internal/controller/ship_group.go b/internal/controller/ship_group.go index 0817a75..956a133 100644 --- a/internal/controller/ship_group.go +++ b/internal/controller/ship_group.go @@ -61,7 +61,7 @@ func (c *Cache) ShipGroupJoinFleet(groupIndex int, fID *uuid.UUID) { func (c *Cache) ShipGroupShipsNumber(groupIndex int, number uint) { c.validateShipGroupIndex(groupIndex) if c.g.ShipGroups[groupIndex].Number > 0 { - c.g.ShipGroups[groupIndex].Load = c.g.ShipGroups[groupIndex].Load / float64(c.g.ShipGroups[groupIndex].Number) * float64(number) + c.g.ShipGroups[groupIndex].Load = game.F(c.g.ShipGroups[groupIndex].Load.F() / float64(c.g.ShipGroups[groupIndex].Number) * float64(number)) } c.g.ShipGroups[groupIndex].Number = number } @@ -228,20 +228,20 @@ func (c *Cache) DisassembleGroup(ri int, groupIndex, quantity uint) error { if c.ShipGroup(sgi).CargoType != nil { ct := *c.ShipGroup(sgi).CargoType - load := c.ShipGroup(sgi).Load + load := c.ShipGroup(sgi).Load.F() switch ct { case game.CargoColonist: if p.Owner == c.g.Race[ri].ID { p = game.UnloadColonists(p, load) } case game.CargoMaterial: - p.Material += load + p.Material = p.Material.Add(load) case game.CargoCapital: - p.Capital += load + p.Capital = p.Capital.Add(load) } } - p.Material += c.ShipGroup(sgi).EmptyMass(st) + p.Material = p.Material.Add(c.ShipGroup(sgi).EmptyMass(st)) c.unsafeDeleteShipGroup(sgi) @@ -300,11 +300,11 @@ func (c *Cache) LoadCargo(ri int, groupIndex uint, ct game.CargoType, ships uint sgi = nsgi } capacity := c.ShipGroup(sgi).CargoCapacity(st) - freeShipGroupCargoLoad := capacity - c.ShipGroup(sgi).Load + freeShipGroupCargoLoad := capacity - c.ShipGroup(sgi).Load.F() if freeShipGroupCargoLoad == 0 { return e.NewCargoLoadNoSpaceLeftError() } - var availableOnPlanet *float64 + var availableOnPlanet *game.Float switch ct { case game.CargoMaterial: availableOnPlanet = &p.Material @@ -315,18 +315,18 @@ func (c *Cache) LoadCargo(ri int, groupIndex uint, ct game.CargoType, ships uint default: return e.NewGameStateError("CargoType not accepted: %v", ct) } - if quantity > *availableOnPlanet || *availableOnPlanet == 0 { + if quantity > float64(*availableOnPlanet) || *availableOnPlanet == 0 { return e.NewCargoLoadNotEnoughError("planet: #%d, %s=%.03f", p.Number, ct, *availableOnPlanet) } toBeLoaded := quantity if quantity == 0 { - toBeLoaded = *availableOnPlanet + toBeLoaded = float64(*availableOnPlanet) } if toBeLoaded > freeShipGroupCargoLoad { toBeLoaded = freeShipGroupCargoLoad } - *availableOnPlanet = *availableOnPlanet - toBeLoaded - c.ShipGroup(sgi).Load += toBeLoaded + *availableOnPlanet = (*availableOnPlanet).Add(-toBeLoaded) + c.ShipGroup(sgi).Load = c.ShipGroup(sgi).Load.Add(toBeLoaded) if c.ShipGroup(sgi).Load > 0 { c.ShipGroup(sgi).CargoType = &ct } @@ -379,9 +379,9 @@ func (c *Cache) UnloadCargo(ri int, groupIndex uint, ships uint, quantity float6 toBeUnloaded := quantity if quantity == 0 { - toBeUnloaded = c.ShipGroup(sgi).Load + toBeUnloaded = c.ShipGroup(sgi).Load.F() } - if toBeUnloaded > c.ShipGroup(sgi).Load { + if toBeUnloaded > c.ShipGroup(sgi).Load.F() { return e.NewCargoUnoadNotEnoughError("load: %.03f", c.ShipGroup(sgi).Load) } @@ -401,7 +401,7 @@ func (c *Cache) unsafeUnloadCargo(sgi int, q float64) { p := c.MustPlanet(c.ShipGroup(sgi).Destination) ct := *c.ShipGroup(sgi).CargoType - var availableOnPlanet *float64 + var availableOnPlanet *game.Float switch ct { case game.CargoColonist: availableOnPlanet = &p.Colonists @@ -414,9 +414,9 @@ func (c *Cache) unsafeUnloadCargo(sgi int, q float64) { case game.CargoCapital: availableOnPlanet = &p.Capital } - *availableOnPlanet += q + *availableOnPlanet = (*availableOnPlanet).Add(q) - c.ShipGroup(sgi).Load -= q // TODO: apply rounding for Load property? + c.ShipGroup(sgi).Load = c.ShipGroup(sgi).Load.Add(-q) // TODO: apply rounding for Load property? if c.ShipGroup(sgi).Load == 0 { c.ShipGroup(sgi).CargoType = nil } @@ -532,7 +532,7 @@ func (c *Cache) breakGroupSafe(ri int, groupIndex uint, newGroupShips uint) (int func (c *Cache) breakGroupUnsafe(ri, sgi int, newGroupShips uint) int { newGroup := *c.ShipGroup(sgi) if c.ShipGroup(sgi).CargoType != nil { - newGroup.Load = c.ShipGroup(sgi).Load / float64(c.ShipGroup(sgi).Number) * float64(newGroupShips) + newGroup.Load = game.F(c.ShipGroup(sgi).Load.F() / float64(c.ShipGroup(sgi).Number) * float64(newGroupShips)) } newGroup.Number = newGroupShips c.ShipGroupShipsNumber(sgi, c.ShipGroup(sgi).Number-newGroup.Number) diff --git a/internal/controller/ship_group_move.go b/internal/controller/ship_group_move.go index 62f1325..cc02dc4 100644 --- a/internal/controller/ship_group_move.go +++ b/internal/controller/ship_group_move.go @@ -40,7 +40,7 @@ func (c *Cache) moveShipGroup(i int, delta float64) { destPlanet := c.MustPlanet(sg.Destination) arrived := false sg.StateInSpace.X, sg.StateInSpace.Y, arrived = - util.NextTravelCoord(c.g.Map.Width, c.g.Map.Height, originX, originY, destPlanet.X, destPlanet.Y, delta) + util.NextTravelCoord(c.g.Map.Width, c.g.Map.Height, originX, originY, destPlanet.X.F(), destPlanet.Y.F(), delta) if arrived { sg.StateInSpace = nil } diff --git a/internal/controller/ship_group_send.go b/internal/controller/ship_group_send.go index 69c9d28..c5cbaa7 100644 --- a/internal/controller/ship_group_send.go +++ b/internal/controller/ship_group_send.go @@ -44,7 +44,7 @@ func (c *Cache) SendGroup(ri int, groupIndex, planetNumber, quantity uint) error if !ok { return e.NewEntityNotExistsError("destination planet #%d", planetNumber) } - rangeToDestination := util.ShortDistance(c.g.Map.Width, c.g.Map.Height, p1.X, p1.Y, p2.X, p2.Y) + rangeToDestination := util.ShortDistance(c.g.Map.Width, c.g.Map.Height, p1.X.F(), p1.Y.F(), p2.X.F(), p2.Y.F()) if rangeToDestination > c.g.Race[ri].FlightDistance() { return e.NewSendUnreachableDestinationError("range=%.03f", rangeToDestination) } @@ -81,7 +81,7 @@ func (c *Cache) LaunchShips(sg *game.ShipGroup, destination uint) *game.ShipGrou default: panic("state invalid") } - c.g.ShipGroups[i] = LaunchShips(*sg, destination, p.X, p.Y) + c.g.ShipGroups[i] = LaunchShips(*sg, destination, p.X.F(), p.Y.F()) return &c.g.ShipGroups[i] } } diff --git a/internal/controller/ship_group_send_test.go b/internal/controller/ship_group_send_test.go index aa97191..586f147 100644 --- a/internal/controller/ship_group_send_test.go +++ b/internal/controller/ship_group_send_test.go @@ -51,8 +51,8 @@ func TestSendGroup(t *testing.T) { assert.Equal(t, uint(3), c.ShipGroup(3).Number) assert.Equal(t, game.StateLaunched, c.ShipGroup(3).State()) assert.NotNil(t, c.ShipGroup(3).StateInSpace) - assert.Equal(t, c.MustPlanet(R0_Planet_0_num).X, c.ShipGroup(3).StateInSpace.X) - assert.Equal(t, c.MustPlanet(R0_Planet_0_num).Y, c.ShipGroup(3).StateInSpace.Y) + assert.Equal(t, c.MustPlanet(R0_Planet_0_num).X.F(), c.ShipGroup(3).StateInSpace.X) + assert.Equal(t, c.MustPlanet(R0_Planet_0_num).Y.F(), c.ShipGroup(3).StateInSpace.Y) assert.NoError(t, g.SendGroup(Race_0.Name, 4, R0_Planet_0_num, 2)) // un-send 2 of 3 assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 4) @@ -61,8 +61,8 @@ func TestSendGroup(t *testing.T) { assert.Equal(t, uint(1), c.MustShipGroup(Race_0_idx, 4).Number) assert.Equal(t, game.StateLaunched, c.MustShipGroup(Race_0_idx, 4).State()) assert.NotNil(t, c.MustShipGroup(Race_0_idx, 4).StateInSpace) - assert.Equal(t, c.MustPlanet(R0_Planet_0_num).X, c.MustShipGroup(Race_0_idx, 4).StateInSpace.X) - assert.Equal(t, c.MustPlanet(R0_Planet_0_num).Y, c.MustShipGroup(Race_0_idx, 4).StateInSpace.Y) + assert.Equal(t, c.MustPlanet(R0_Planet_0_num).X.F(), c.MustShipGroup(Race_0_idx, 4).StateInSpace.X) + assert.Equal(t, c.MustPlanet(R0_Planet_0_num).Y.F(), c.MustShipGroup(Race_0_idx, 4).StateInSpace.Y) assert.NoError(t, g.SendGroup(Race_0.Name, 4, R0_Planet_0_num, 0)) // un-send the rest 1 assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 3) diff --git a/internal/controller/ship_group_test.go b/internal/controller/ship_group_test.go index cfbe9f6..a389207 100644 --- a/internal/controller/ship_group_test.go +++ b/internal/controller/ship_group_test.go @@ -142,9 +142,9 @@ func TestBreakGroup(t *testing.T) { assert.NotNil(t, c.ShipGroup(3).FleetID) assert.Equal(t, game.CargoColonist.Ref(), c.ShipGroup(0).CargoType) - assert.Equal(t, 24.6, number.Fixed3(c.ShipGroup(0).Load)) + assert.Equal(t, 24.6, number.Fixed3(c.ShipGroup(0).Load.F())) assert.Equal(t, game.CargoColonist.Ref(), c.ShipGroup(3).CargoType) - assert.Equal(t, 8.2, number.Fixed3(c.ShipGroup(3).Load)) + assert.Equal(t, 8.2, number.Fixed3(c.ShipGroup(3).Load.F())) // group #1 -> MAX 6 off the fleet assert.NoError(t, g.BreakGroup(Race_0.Name, 1, 6)) // group #1 (0) @@ -295,48 +295,48 @@ func TestLoadCargo(t *testing.T) { // break group and load maximum assert.NoError(t, g.LoadCargo(Race_0.Name, 1, game.CargoMaterial.String(), 2, 0)) - assert.Equal(t, 58.0, c.MustPlanet(R0_Planet_0_num).Material) + assert.Equal(t, 58.0, c.MustPlanet(R0_Planet_0_num).Material.F()) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 6) assert.Nil(t, c.ShipGroup(0).CargoType) assert.Equal(t, game.CargoMaterial.Ref(), c.ShipGroup(5).CargoType) assert.Equal(t, uint(9), c.ShipGroup(0).Number) - assert.Equal(t, 0.0, c.ShipGroup(0).Load) + assert.Equal(t, 0.0, c.ShipGroup(0).Load.F()) assert.Equal(t, uint(2), c.ShipGroup(5).Number) - assert.Equal(t, 42.0, c.ShipGroup(5).Load) + assert.Equal(t, 42.0, c.ShipGroup(5).Load.F()) // break group and load limited assert.NoError(t, g.LoadCargo(Race_0.Name, 1, game.CargoMaterial.String(), 2, 18)) - assert.Equal(t, 40.0, c.MustPlanet(R0_Planet_0_num).Material) + assert.Equal(t, 40.0, c.MustPlanet(R0_Planet_0_num).Material.F()) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 7) assert.Nil(t, c.ShipGroup(0).CargoType) assert.Equal(t, game.CargoMaterial.Ref(), c.ShipGroup(6).CargoType) assert.Equal(t, uint(7), c.ShipGroup(0).Number) - assert.Equal(t, 0.0, c.ShipGroup(0).Load) + assert.Equal(t, 0.0, c.ShipGroup(0).Load.F()) assert.Equal(t, uint(2), c.ShipGroup(6).Number) - assert.Equal(t, 18.0, c.ShipGroup(6).Load) + assert.Equal(t, 18.0, c.ShipGroup(6).Load.F()) // add cargo to planet c.PutMaterial(R0_Planet_0_num, 100) // loading all available cargo assert.NoError(t, g.LoadCargo(Race_0.Name, 1, game.CargoMaterial.String(), 0, 0)) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 7) - assert.Equal(t, 0.0, c.MustPlanet(R0_Planet_0_num).Material) - assert.Equal(t, 100.0, c.ShipGroup(0).Load) // free: 131.0 + assert.Equal(t, 0.0, c.MustPlanet(R0_Planet_0_num).Material.F()) + assert.Equal(t, 100.0, c.ShipGroup(0).Load.F()) // free: 131.0 assert.Equal(t, game.CargoMaterial.Ref(), c.ShipGroup(0).CargoType) // add cargo to planet c.PutMaterial(R0_Planet_0_num, 200) assert.NoError(t, g.LoadCargo(Race_0.Name, 1, game.CargoMaterial.String(), 11, 31)) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 7) - assert.Equal(t, 169.0, c.MustPlanet(R0_Planet_0_num).Material) - assert.Equal(t, 131.0, c.ShipGroup(0).Load) // free: 100.0 + assert.Equal(t, 169.0, c.MustPlanet(R0_Planet_0_num).Material.F()) + assert.Equal(t, 131.0, c.ShipGroup(0).Load.F()) // free: 100.0 assert.Equal(t, game.CargoMaterial.Ref(), c.ShipGroup(0).CargoType) // load to maximum cargo space left assert.NoError(t, g.LoadCargo(Race_0.Name, 1, game.CargoMaterial.String(), 11, 0)) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 7) - assert.Equal(t, 153.0, c.MustPlanet(R0_Planet_0_num).Material) - assert.Equal(t, 147.0, c.ShipGroup(0).Load) // free: 0.0 + assert.Equal(t, 153.0, c.MustPlanet(R0_Planet_0_num).Material.F()) + assert.Equal(t, 147.0, c.ShipGroup(0).Load.F()) // free: 0.0 assert.Equal(t, game.CargoMaterial.Ref(), c.ShipGroup(0).CargoType) // ship group is full @@ -413,33 +413,33 @@ func TestUnloadCargo(t *testing.T) { // unload MAT on foreign planet / break group assert.NoError(t, g.UnloadCargo(Race_0.Name, 6, 3, 0)) - assert.Equal(t, 27.273, number.Fixed3(c.MustPlanet(R1_Planet_1_num).Material)) + assert.Equal(t, 27.273, number.Fixed3(c.MustPlanet(R1_Planet_1_num).Material.F())) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 7) assert.Equal(t, uint(3), c.ShipGroup(6).Number) assert.Nil(t, c.ShipGroup(6).CargoType) - assert.Equal(t, 0.0, c.ShipGroup(6).Load) + assert.Equal(t, 0.0, c.ShipGroup(6).Load.F()) assert.Equal(t, game.CargoMaterial.Ref(), c.ShipGroup(5).CargoType) assert.Equal(t, uint(8), c.ShipGroup(5).Number) - assert.Equal(t, 72.727, number.Fixed3(c.ShipGroup(5).Load)) + assert.Equal(t, 72.727, number.Fixed3(c.ShipGroup(5).Load.F())) // unload MAT on foreign planet / break group / limited MAT assert.NoError(t, g.UnloadCargo(Race_0.Name, 6, 3, 20.0)) - assert.Equal(t, 47.273, number.Fixed3(c.MustPlanet(R1_Planet_1_num).Material)) + assert.Equal(t, 47.273, number.Fixed3(c.MustPlanet(R1_Planet_1_num).Material.F())) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 8) assert.Equal(t, uint(3), c.ShipGroup(7).Number) assert.Equal(t, game.CargoMaterial.Ref(), c.ShipGroup(7).CargoType) - assert.Equal(t, 7.273, number.Fixed3(c.ShipGroup(7).Load)) + assert.Equal(t, 7.273, number.Fixed3(c.ShipGroup(7).Load.F())) assert.Equal(t, game.CargoMaterial.Ref(), c.ShipGroup(5).CargoType) assert.Equal(t, uint(5), c.ShipGroup(5).Number) - assert.Equal(t, 45.455, number.Fixed3(c.ShipGroup(5).Load)) + assert.Equal(t, 45.455, number.Fixed3(c.ShipGroup(5).Load.F())) // unload ALL assert.NoError(t, g.UnloadCargo(Race_0.Name, 1, 0, 0)) - assert.Equal(t, 100.0, number.Fixed3(c.MustPlanet(R0_Planet_0_num).Colonists)) + assert.Equal(t, 100.0, number.Fixed3(c.MustPlanet(R0_Planet_0_num).Colonists.F())) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 8) assert.Equal(t, uint(10), c.ShipGroup(0).Number) assert.Nil(t, c.ShipGroup(0).CargoType) - assert.Equal(t, 0.0, number.Fixed3(c.ShipGroup(0).Load)) + assert.Equal(t, 0.0, number.Fixed3(c.ShipGroup(0).Load.F())) } func TestDisassembleGroup(t *testing.T) { @@ -489,40 +489,40 @@ func TestDisassembleGroup(t *testing.T) { e.GenericErrorText(e.ErrBeakGroupNumberNotEnough)) groupEmptyMass := c.ShipGroup(4).EmptyMass(c.MustShipClass(Race_0_idx, Race_0_Freighter)) - planetMAT := c.MustPlanet(R1_Planet_1_num).Material - planetCOL := c.MustPlanet(R1_Planet_1_num).Colonists + planetMAT := c.MustPlanet(R1_Planet_1_num).Material.F() + planetCOL := c.MustPlanet(R1_Planet_1_num).Colonists.F() assert.NoError(t, g.DisassembleGroup(Race_0.Name, 5, 0)) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 4) - assert.Equal(t, planetMAT+groupEmptyMass, c.MustPlanet(R1_Planet_1_num).Material) - assert.Equal(t, planetCOL, c.MustPlanet(R1_Planet_1_num).Colonists) + assert.Equal(t, planetMAT+groupEmptyMass, c.MustPlanet(R1_Planet_1_num).Material.F()) + assert.Equal(t, planetCOL, c.MustPlanet(R1_Planet_1_num).Colonists.F()) groupEmptyMass = c.ShipGroup(3).EmptyMass(c.MustShipClass(Race_0_idx, Race_0_Freighter)) - groupLoadMAT := c.ShipGroup(3).Load - planetMAT = c.MustPlanet(R1_Planet_1_num).Material + groupLoadMAT := c.ShipGroup(3).Load.F() + planetMAT = c.MustPlanet(R1_Planet_1_num).Material.F() assert.NoError(t, g.DisassembleGroup(Race_0.Name, 4, 0)) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 3) - assert.Equal(t, planetMAT+groupEmptyMass+groupLoadMAT, c.MustPlanet(R1_Planet_1_num).Material) + assert.Equal(t, planetMAT+groupEmptyMass+groupLoadMAT, c.MustPlanet(R1_Planet_1_num).Material.F()) groupEmptyMass = c.ShipGroup(2).EmptyMass(c.MustShipClass(Race_0_idx, Race_0_Freighter)) - planetMAT = c.MustPlanet(R0_Planet_0_num).Material - planetCOL = c.MustPlanet(R0_Planet_0_num).Colonists + planetMAT = c.MustPlanet(R0_Planet_0_num).Material.F() + planetCOL = c.MustPlanet(R0_Planet_0_num).Colonists.F() planetPOP := 90.0 c.PutPopulation(R0_Planet_0_num, planetPOP) - assert.Equal(t, planetPOP, c.MustPlanet(R0_Planet_0_num).Population) + assert.Equal(t, planetPOP, c.MustPlanet(R0_Planet_0_num).Population.F()) var quantity uint = 3 groupEmptyMass = groupEmptyMass / float64(c.ShipGroup(2).Number) * float64(quantity) - newGroupUnloadedCOL := c.ShipGroup(2).Load / float64(c.ShipGroup(2).Number) * float64(quantity) + newGroupUnloadedCOL := c.ShipGroup(2).Load.F() / float64(c.ShipGroup(2).Number) * float64(quantity) expectPOPIncrease := newGroupUnloadedCOL * 8 - freePOPLeft := c.MustPlanet(R0_Planet_0_num).Size - c.MustPlanet(R0_Planet_0_num).Population + freePOPLeft := c.MustPlanet(R0_Planet_0_num).Size.F() - c.MustPlanet(R0_Planet_0_num).Population.F() expectAddedCOL := (expectPOPIncrease - freePOPLeft) / 8 expectAddedPOP := freePOPLeft assert.NoError(t, g.DisassembleGroup(Race_0.Name, 3, quantity)) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 3) - assert.Equal(t, planetCOL+expectAddedCOL, c.MustPlanet(R0_Planet_0_num).Colonists) - assert.Equal(t, planetPOP+expectAddedPOP, c.MustPlanet(R0_Planet_0_num).Population) - assert.Equal(t, planetMAT+groupEmptyMass, c.MustPlanet(R0_Planet_0_num).Material) + assert.Equal(t, planetCOL+expectAddedCOL, c.MustPlanet(R0_Planet_0_num).Colonists.F()) + assert.Equal(t, planetPOP+expectAddedPOP, c.MustPlanet(R0_Planet_0_num).Population.F()) + assert.Equal(t, planetMAT+groupEmptyMass, c.MustPlanet(R0_Planet_0_num).Material.F()) assert.Equal(t, uint(7), c.ShipGroup(2).Number) - assert.Equal(t, 56.0, c.ShipGroup(2).Load) + assert.Equal(t, 56.0, c.ShipGroup(2).Load.F()) } diff --git a/internal/controller/ship_group_upgrade.go b/internal/controller/ship_group_upgrade.go index bd0714f..e027802 100644 --- a/internal/controller/ship_group_upgrade.go +++ b/internal/controller/ship_group_upgrade.go @@ -65,7 +65,7 @@ func (c *Cache) UpgradeGroup(ri int, groupIndex uint, techInput string, limitShi if c.g.Race[ri].TechLevel(tech) < limitLevel { return e.NewUpgradeTechLevelInsufficientError("%s=%.03f < %.03f", tech.String(), c.g.Race[ri].TechLevel(tech), limitLevel) } - targetLevel[tech] = game.FutureUpgradeLevel(c.g.Race[ri].TechLevel(tech), c.ShipGroup(sgi).TechLevel(tech), limitLevel) + targetLevel[tech] = game.FutureUpgradeLevel(c.g.Race[ri].TechLevel(tech), c.ShipGroup(sgi).TechLevel(tech).F(), limitLevel) } else { targetLevel[tech] = game.CurrentUpgradingLevel(c.g.ShipGroups[sgi], tech) } diff --git a/internal/model/game/group.go b/internal/model/game/group.go index 739b5e4..ab43bb0 100644 --- a/internal/model/game/group.go +++ b/internal/model/game/group.go @@ -107,7 +107,7 @@ type ShipGroup struct { Number uint `json:"number"` // Number (quantity) ships of specific ShipType CargoType *CargoType `json:"loadType,omitempty"` - Load float64 `json:"load"` // Cargo loaded - "Масса груза" + Load Float `json:"load"` // Cargo loaded - "Масса груза" Tech TechSet `json:"tech"` @@ -117,8 +117,8 @@ type ShipGroup struct { StateUpgrade *InUpgrade `json:"stateUpgrade,omitempty"` } -func (sg ShipGroup) TechLevel(t Tech) float64 { - return sg.Tech.Value(t) +func (sg ShipGroup) TechLevel(t Tech) Float { + return F(sg.Tech.Value(t)) } // TODO: refactor to separate method with *ShipGroup as parameter @@ -170,19 +170,19 @@ func (sg ShipGroup) Equal(other ShipGroup) bool { sg.TechLevel(TechShields) == other.TechLevel(TechShields) && sg.TechLevel(TechCargo) == other.TechLevel(TechCargo) && sg.CargoType == other.CargoType && - sg.Load/float64(sg.Number) == other.Load/float64(other.Number) && + sg.Load.F()/float64(sg.Number) == other.Load.F()/float64(other.Number) && sg.State() == other.State() } // Грузоподъёмность func (sg ShipGroup) CargoCapacity(st *ShipType) float64 { - return sg.TechLevel(TechCargo) * (st.Cargo + (st.Cargo*st.Cargo)/20) * float64(sg.Number) + return sg.TechLevel(TechCargo).F() * (st.Cargo + (st.Cargo*st.Cargo)/20) * float64(sg.Number) } // Масса перевозимого груза - // общее количество единиц груза, деленное на технологический уровень Грузоперевозок func (sg ShipGroup) CarryingMass() float64 { - return sg.Load / sg.TechLevel(TechCargo) + return sg.Load.F() / sg.TechLevel(TechCargo).F() } // Масса группы без учёта груза @@ -199,7 +199,7 @@ func (sg ShipGroup) FullMass(st *ShipType) float64 { // Эффективность двигателя - // равна мощности Двигателей, умноженной на технологический уровень блока Двигателей func (sg ShipGroup) DriveEffective(st *ShipType) float64 { - return st.Drive * sg.TechLevel(TechDrive) + return st.Drive * sg.TechLevel(TechDrive).F() } // Корабли перемещаются за один ход на количество световых лет, равное @@ -209,27 +209,27 @@ func (sg ShipGroup) Speed(st *ShipType) float64 { } func (sg ShipGroup) UpgradeDriveCost(st *ShipType, drive float64) float64 { - return (1 - sg.TechLevel(TechDrive)/drive) * 10 * st.Drive + return (1 - sg.TechLevel(TechDrive).F()/drive) * 10 * st.Drive } // TODO: test on other values func (sg ShipGroup) UpgradeWeaponsCost(st *ShipType, weapons float64) float64 { - return (1 - sg.TechLevel(TechWeapons)/weapons) * 10 * st.WeaponsBlockMass() + return (1 - sg.TechLevel(TechWeapons).F()/weapons) * 10 * st.WeaponsBlockMass() } func (sg ShipGroup) UpgradeShieldsCost(st *ShipType, shields float64) float64 { - return (1 - sg.TechLevel(TechShields)/shields) * 10 * st.Shields + return (1 - sg.TechLevel(TechShields).F()/shields) * 10 * st.Shields } func (sg ShipGroup) UpgradeCargoCost(st *ShipType, cargo float64) float64 { - return (1 - sg.TechLevel(TechCargo)/cargo) * 10 * st.Cargo + return (1 - sg.TechLevel(TechCargo).F()/cargo) * 10 * st.Cargo } // Мощность бомбардировки func (sg ShipGroup) BombingPower(st *ShipType) float64 { - return (math.Sqrt(st.Weapons*sg.TechLevel(TechWeapons))/10. + 1.) * + return (math.Sqrt(st.Weapons*sg.TechLevel(TechWeapons).F())/10. + 1.) * st.Weapons * - sg.TechLevel(TechWeapons) * + sg.TechLevel(TechWeapons).F() * float64(st.Armament) * float64(sg.Number) } diff --git a/internal/model/game/group_test.go b/internal/model/game/group_test.go index d91f6d8..c4ea07a 100644 --- a/internal/model/game/group_test.go +++ b/internal/model/game/group_test.go @@ -228,22 +228,22 @@ func TestShipGroupEqual(t *testing.T) { right = *left left.SetTechLevel(game.TechDrive, 1.1) - assert.Equal(t, 1.1, left.TechLevel(game.TechDrive)) + assert.Equal(t, 1.1, left.TechLevel(game.TechDrive).F()) assert.False(t, left.Equal(right)) right = *left left.SetTechLevel(game.TechWeapons, 1.1) - assert.Equal(t, 1.1, left.TechLevel(game.TechWeapons)) + assert.Equal(t, 1.1, left.TechLevel(game.TechWeapons).F()) assert.False(t, left.Equal(right)) right = *left left.SetTechLevel(game.TechShields, 1.1) - assert.Equal(t, 1.1, left.TechLevel(game.TechShields)) + assert.Equal(t, 1.1, left.TechLevel(game.TechShields).F()) assert.False(t, left.Equal(right)) right = *left left.SetTechLevel(game.TechCargo, 1.1) - assert.Equal(t, 1.1, left.TechLevel(game.TechCargo)) + assert.Equal(t, 1.1, left.TechLevel(game.TechCargo).F()) assert.False(t, left.Equal(right)) // non-essential properties @@ -254,6 +254,6 @@ func TestShipGroupEqual(t *testing.T) { // dirty hack to equalize loads left.Number = 5 - left.Load = right.Load / float64(right.Number) * float64(left.Number) + left.Load = game.F(right.Load.F() / float64(right.Number) * float64(left.Number)) assert.True(t, left.Equal(right)) } diff --git a/internal/model/game/group_upgrade.go b/internal/model/game/group_upgrade.go index 670920c..6e28e61 100644 --- a/internal/model/game/group_upgrade.go +++ b/internal/model/game/group_upgrade.go @@ -32,16 +32,16 @@ func BlockUpgradeCost(blockMass, currentBlockTech, targetBlockTech float64) floa func GroupUpgradeCost(sg ShipGroup, st ShipType, drive, weapons, shields, cargo float64) UpgradeCalc { uc := &UpgradeCalc{Cost: make(map[Tech]float64)} if drive > 0 { - uc.Cost[TechDrive] = BlockUpgradeCost(st.DriveBlockMass(), sg.TechLevel(TechDrive), drive) + uc.Cost[TechDrive] = BlockUpgradeCost(st.DriveBlockMass(), sg.TechLevel(TechDrive).F(), drive) } if weapons > 0 { - uc.Cost[TechWeapons] = BlockUpgradeCost(st.WeaponsBlockMass(), sg.TechLevel(TechWeapons), weapons) + uc.Cost[TechWeapons] = BlockUpgradeCost(st.WeaponsBlockMass(), sg.TechLevel(TechWeapons).F(), weapons) } if shields > 0 { - uc.Cost[TechShields] = BlockUpgradeCost(st.ShieldsBlockMass(), sg.TechLevel(TechShields), shields) + uc.Cost[TechShields] = BlockUpgradeCost(st.ShieldsBlockMass(), sg.TechLevel(TechShields).F(), shields) } if cargo > 0 { - uc.Cost[TechCargo] = BlockUpgradeCost(st.CargoBlockMass(), sg.TechLevel(TechCargo), cargo) + uc.Cost[TechCargo] = BlockUpgradeCost(st.CargoBlockMass(), sg.TechLevel(TechCargo).F(), cargo) } return *uc } @@ -69,7 +69,7 @@ func FutureUpgradeLevel(raceLevel, groupLevel, limit float64) float64 { } func UpgradeGroupPreference(sg ShipGroup, st ShipType, tech Tech, v float64) ShipGroup { - if v <= 0 || st.BlockMass(tech) == 0 || sg.TechLevel(tech) >= v { + if v <= 0 || st.BlockMass(tech) == 0 || sg.TechLevel(tech).F() >= v { return sg } var su InUpgrade @@ -84,7 +84,7 @@ func UpgradeGroupPreference(sg ShipGroup, st ShipType, tech Tech, v float64) Shi ti = len(su.UpgradeTech) - 1 } su.UpgradeTech[ti].Level = v - su.UpgradeTech[ti].Cost = BlockUpgradeCost(st.BlockMass(tech), sg.TechLevel(tech), v) * float64(sg.Number) + su.UpgradeTech[ti].Cost = BlockUpgradeCost(st.BlockMass(tech), sg.TechLevel(tech).F(), v) * float64(sg.Number) sg.StateUpgrade = &su return sg diff --git a/internal/model/game/planet.go b/internal/model/game/planet.go index 11b0210..cede4ba 100644 --- a/internal/model/game/planet.go +++ b/internal/model/game/planet.go @@ -7,25 +7,25 @@ import ( ) type UnidentifiedPlanet struct { - X float64 `json:"x"` - Y float64 `json:"y"` - Number uint `json:"number"` + X Float `json:"x"` + Y Float `json:"y"` + Number uint `json:"number"` } type UninhabitedPlanet struct { UnidentifiedPlanet - Size float64 `json:"size"` - Name string `json:"name"` - Resources float64 `json:"resources"` // R - Ресурсы - Capital float64 `json:"capital"` // CAP $ - Запасы промышленности - Material float64 `json:"material"` // MAT M - Запасы ресурсов / сырьё + Size Float `json:"size"` + Name string `json:"name"` + Resources Float `json:"resources"` // R - Ресурсы + Capital Float `json:"capital"` // CAP $ - Запасы промышленности + Material Float `json:"material"` // MAT M - Запасы ресурсов / сырьё } type PlanetReport struct { UninhabitedPlanet - Industry float64 `json:"industry"` // I - Промышленность - Population float64 `json:"population"` // P - Население - Colonists float64 `json:"colonists"` // COL C - Количество колонистов + Industry Float `json:"industry"` // I - Промышленность + Population Float `json:"population"` // P - Население + Colonists Float `json:"colonists"` // COL C - Количество колонистов Production Production `json:"production"` // TODO: internal/report format // Параметр "L" - Свободный производственный потенциал } @@ -41,13 +41,33 @@ type PlanetReportForeign struct { PlanetReport } +func (p *Planet) Mat(v float64) { + p.Material = F(v) +} + +func (p *Planet) Pop(v float64) { + p.Population = F(v) +} + +func (p *Planet) Col(v float64) { + p.Colonists = F(v) +} + +func (p *Planet) Ind(v float64) { + p.Industry = F(v) +} + +func (p *Planet) Cap(v float64) { + p.Capital = F(v) +} + func (p Planet) Votes() float64 { - return p.Population / 1000. + return p.Population.F() / 1000. } // Производственный потенциал func (p Planet) ProductionCapacity() float64 { - return PlanetProduction(p.Industry, p.Population) + return PlanetProduction(p.Industry.F(), p.Population.F()) } func PlanetProduction(industry, population float64) float64 { @@ -64,12 +84,12 @@ func (p *Planet) ProduceIndustry() { production := p.ProductionCapacity() var ind float64 if p.Material > 0 { - ind = math.Min(production/5, p.Material) - p.Material -= ind + ind = math.Min(production/5, p.Material.F()) + p.Mat(p.Material.F() - ind) production -= ind * 5. } - ind += (production * p.Resources) / (5.*p.Resources + 1.) - p.Industry += ind + ind += (production * p.Resources.F()) / (5.*p.Resources.F() + 1.) + p.Ind(p.Industry.F() + ind) if p.Industry > p.Population { p.Capital += p.Industry - p.Population p.Industry = p.Population @@ -78,7 +98,7 @@ func (p *Planet) ProduceIndustry() { // Производство материалов func (p *Planet) ProduceMaterial() { - p.Material += p.ProductionCapacity() * p.Resources + p.Material = p.Material.Add(p.ProductionCapacity() * p.Resources.F()) } // Автоматическое увеличение населения на каждом ходу @@ -115,10 +135,10 @@ func (p *Planet) UnpackColonists() { } func UnloadColonists(p Planet, v float64) Planet { - p.Population += v * 8 + p.Pop(p.Population.F() + v*8) if p.Population > p.Size { - p.Colonists += (p.Population - p.Size) / 8 - p.Population = p.Size + p.Col(p.Colonists.F() + (p.Population.F()-p.Size.F())/8.) + p.Pop(p.Size.F()) } return p } diff --git a/internal/model/game/planet_test.go b/internal/model/game/planet_test.go index cc66cbf..3a681bf 100644 --- a/internal/model/game/planet_test.go +++ b/internal/model/game/planet_test.go @@ -36,24 +36,24 @@ func TestProduceIndustry(t *testing.T) { }, } HW.ProduceIndustry() - assert.InDelta(t, 196.078, HW.Capital, 0.0005) + assert.InDelta(t, 196.078, HW.Capital.F(), 0.0005) HW.Capital = 0 HW.Material = 200 HW.ProduceIndustry() - assert.Equal(t, 200., HW.Capital) - assert.Equal(t, 0., HW.Material) + assert.Equal(t, 200., HW.Capital.F()) + assert.Equal(t, 0., HW.Material.F()) DW.ProduceIndustry() - assert.InDelta(t, 98.039, DW.Capital, 0.0003) + assert.InDelta(t, 98.039, DW.Capital.F(), 0.0003) DW.Capital = 0 DW.Material = 100 DW.ProduceIndustry() - assert.Equal(t, 100., DW.Capital) - assert.Equal(t, 0., DW.Material) + assert.Equal(t, 100., DW.Capital.F()) + assert.Equal(t, 0., DW.Material.F()) } func TestProduceMaterial(t *testing.T) { @@ -67,19 +67,19 @@ func TestProduceMaterial(t *testing.T) { Industry: 1000, }, } - assert.Equal(t, 0., HW.Material) + assert.Equal(t, 0., HW.Material.F()) HW.ProduceMaterial() - assert.Equal(t, 10000., HW.Material) + assert.Equal(t, 10000., HW.Material.F()) HW.Industry = 500 HW.Population = 500 HW.ProduceMaterial() - assert.Equal(t, 15000., HW.Material) + assert.Equal(t, 15000., HW.Material.F()) HW.Population = 1000 HW.ProduceMaterial() - assert.Equal(t, 21250., HW.Material) + assert.Equal(t, 21250., HW.Material.F()) } func TestUnpackCapital(t *testing.T) { @@ -93,28 +93,28 @@ func TestUnpackCapital(t *testing.T) { Industry: 1000, }, } - assert.Equal(t, 0., HW.Capital) + assert.Equal(t, 0., HW.Capital.F()) HW.UnpackCapital() - assert.Equal(t, 1000., HW.Industry) - assert.Equal(t, 0., HW.Capital) + assert.Equal(t, 1000., HW.Industry.F()) + assert.Equal(t, 0., HW.Capital.F()) HW.Capital = 123. HW.UnpackCapital() - assert.Equal(t, 1000., HW.Industry) - assert.Equal(t, 123., HW.Capital) + assert.Equal(t, 1000., HW.Industry.F()) + assert.Equal(t, 123., HW.Capital.F()) HW.Industry = 987. HW.UnpackCapital() - assert.Equal(t, 1000., HW.Industry) - assert.Equal(t, 110., HW.Capital) + assert.Equal(t, 1000., HW.Industry.F()) + assert.Equal(t, 110., HW.Capital.F()) HW.Population = 876. HW.Industry = 800. HW.UnpackCapital() - assert.Equal(t, 876., HW.Population) - assert.Equal(t, 876., HW.Industry) - assert.Equal(t, 34., HW.Capital) + assert.Equal(t, 876., HW.Population.F()) + assert.Equal(t, 876., HW.Industry.F()) + assert.Equal(t, 34., HW.Capital.F()) } func TestUnpackColonists(t *testing.T) { @@ -128,26 +128,26 @@ func TestUnpackColonists(t *testing.T) { Industry: 1000, }, } - assert.Equal(t, 0., HW.Colonists) + assert.Equal(t, 0., HW.Colonists.F()) HW.UnpackColonists() - assert.Equal(t, 1000., HW.Population) - assert.Equal(t, 0., HW.Colonists) + assert.Equal(t, 1000., HW.Population.F()) + assert.Equal(t, 0., HW.Colonists.F()) HW.Colonists = 1.05 HW.UnpackColonists() - assert.Equal(t, 1000., HW.Population) - assert.Equal(t, 1.05, HW.Colonists) + assert.Equal(t, 1000., HW.Population.F()) + assert.Equal(t, 1.05, HW.Colonists.F()) HW.Population = 996.0 HW.UnpackColonists() - assert.Equal(t, 1000., HW.Population) - assert.Equal(t, 0.55, HW.Colonists) + assert.Equal(t, 1000., HW.Population.F()) + assert.Equal(t, 0.55, HW.Colonists.F()) HW.Population = 0.0 HW.UnpackColonists() - assert.Equal(t, 4.4, HW.Population) - assert.Equal(t, 0., HW.Colonists) + assert.Equal(t, 4.4, HW.Population.F()) + assert.Equal(t, 0., HW.Colonists.F()) } func TestProducePopulation(t *testing.T) { @@ -161,15 +161,15 @@ func TestProducePopulation(t *testing.T) { Industry: 1000, }, } - assert.Equal(t, 500., HW.Population) - assert.Equal(t, 0., HW.Colonists) + assert.Equal(t, 500., HW.Population.F()) + assert.Equal(t, 0., HW.Colonists.F()) HW.ProducePopulation() - assert.Equal(t, 540., HW.Population) - assert.Equal(t, 0., HW.Colonists) + assert.Equal(t, 540., HW.Population.F()) + assert.Equal(t, 0., HW.Colonists.F()) HW.Population = 1000. HW.ProducePopulation() - assert.Equal(t, 1000., HW.Population) - assert.Equal(t, 10., HW.Colonists) + assert.Equal(t, 1000., HW.Population.F()) + assert.Equal(t, 10., HW.Colonists.F()) } diff --git a/internal/model/report/battle.go b/internal/model/report/battle.go index 26bc1f3..5889701 100644 --- a/internal/model/report/battle.go +++ b/internal/model/report/battle.go @@ -23,11 +23,11 @@ type BattleReportGroup struct { NumberLeft uint `json:"numLeft"` ClassName string `json:"className"` LoadType string `json:"loadType"` - LoadQuantity float64 `json:"loadQuantity"` - Drive float64 `json:"drive"` - Weapons float64 `json:"wwapons"` - Shields float64 `json:"shields"` - Cargo float64 `json:"cargo"` + LoadQuantity Float `json:"loadQuantity"` + Drive Float `json:"drive"` + Weapons Float `json:"wwapons"` + Shields Float `json:"shields"` + Cargo Float `json:"cargo"` } type BattleActionReport struct { diff --git a/internal/model/report/bombing.go b/internal/model/report/bombing.go index b79f716..2903a01 100644 --- a/internal/model/report/bombing.go +++ b/internal/model/report/bombing.go @@ -9,11 +9,11 @@ type BombingPlanetReport struct { Owner string `json:"owner"` Attacker string `json:"attacker"` Production string `json:"production"` - Industry float64 `json:"industry"` // I - Промышленность - Population float64 `json:"population"` // P - Население - Colonists float64 `json:"colonists"` // COL C - Количество колонистов - Capital float64 `json:"capital"` // CAP $ - Запасы промышленности - Material float64 `json:"material"` // MAT M - Запасы ресурсов / сырья - AttackPower float64 `json:"attack"` + Industry Float `json:"industry"` // I - Промышленность + Population Float `json:"population"` // P - Население + Colonists Float `json:"colonists"` // COL C - Количество колонистов + Capital Float `json:"capital"` // CAP $ - Запасы промышленности + Material Float `json:"material"` // MAT M - Запасы ресурсов / сырья + AttackPower Float `json:"attack"` Wiped bool `json:"wiped"` } diff --git a/internal/repo/game.go b/internal/repo/game.go index 1fbeae3..6f3a769 100644 --- a/internal/repo/game.go +++ b/internal/repo/game.go @@ -38,18 +38,9 @@ func saveTurn(s Storage, t uint, g *game.Game) error { if err := s.Write(path, g); err != nil { return NewStorageError(err) } - // TODO: save reports - for i := range g.Race { - saveRace(s, g, i) - } - // TODO: save battles return saveState(s, g) // FIXME: either save it here, or in tre repo controller } -func saveRace(s Storage, g *game.Game, i int) { - -} - func (r *repo) SaveState(g *game.Game) error { return saveState(r.s, g) }