package controller_test import ( "slices" "testing" "github.com/iliadenisov/galaxy/internal/controller" e "github.com/iliadenisov/galaxy/internal/error" "github.com/iliadenisov/galaxy/internal/model/game" "github.com/stretchr/testify/assert" ) func TestSetRoute(t *testing.T) { c, g := newCache() assert.NotContains(t, c.MustPlanet(0).Route, game.RouteMaterial) assert.NotContains(t, c.MustPlanet(0).Route, game.RouteCapital) assert.NotContains(t, c.MustPlanet(0).Route, game.RouteColonist) assert.NotContains(t, c.MustPlanet(0).Route, game.RouteEmpty) assert.NoError(t, g.SetRoute(Race_0.Name, "COL", 0, 2)) assert.NotContains(t, c.MustPlanet(0).Route, game.RouteMaterial) assert.NotContains(t, c.MustPlanet(0).Route, game.RouteCapital) assert.Contains(t, c.MustPlanet(0).Route, game.RouteColonist) assert.NotContains(t, c.MustPlanet(0).Route, game.RouteEmpty) assert.NoError(t, g.SetRoute(Race_0.Name, "MAT", 0, 2)) assert.Contains(t, c.MustPlanet(0).Route, game.RouteMaterial) assert.NotContains(t, c.MustPlanet(0).Route, game.RouteCapital) assert.Contains(t, c.MustPlanet(0).Route, game.RouteColonist) assert.NotContains(t, c.MustPlanet(0).Route, game.RouteEmpty) assert.NoError(t, g.SetRoute(Race_0.Name, "CAP", 0, 2)) assert.Contains(t, c.MustPlanet(0).Route, game.RouteMaterial) assert.Contains(t, c.MustPlanet(0).Route, game.RouteCapital) assert.Contains(t, c.MustPlanet(0).Route, game.RouteColonist) assert.NotContains(t, c.MustPlanet(0).Route, game.RouteEmpty) assert.NoError(t, g.SetRoute(Race_0.Name, "EMP", 0, 2)) assert.Contains(t, c.MustPlanet(0).Route, game.RouteMaterial) assert.Contains(t, c.MustPlanet(0).Route, game.RouteCapital) assert.Contains(t, c.MustPlanet(0).Route, game.RouteColonist) assert.Contains(t, c.MustPlanet(0).Route, game.RouteEmpty) assert.ErrorContains(t, g.SetRoute("UnknownRace", "COL", 0, 2), e.GenericErrorText(e.ErrInputUnknownRace)) assert.ErrorContains(t, g.SetRoute(Race_0.Name, "IND", 0, 2), e.GenericErrorText(e.ErrInputCargoTypeInvalid)) assert.ErrorContains(t, g.SetRoute(Race_0.Name, "COL", 500, 2), e.GenericErrorText(e.ErrInputEntityNotExists)) assert.ErrorContains(t, g.SetRoute(Race_0.Name, "COL", 1, 2), e.GenericErrorText(e.ErrInputEntityNotOwned)) assert.ErrorContains(t, g.SetRoute(Race_0.Name, "COL", 0, 3), e.GenericErrorText(e.ErrSendUnreachableDestination)) } func TestRemoveRoute(t *testing.T) { c, g := newCache() assert.NoError(t, g.SetRoute(Race_0.Name, "COL", 0, 2)) assert.NoError(t, g.SetRoute(Race_0.Name, "CAP", 0, 2)) assert.NoError(t, g.SetRoute(Race_0.Name, "EMP", 2, 0)) assert.Contains(t, c.MustPlanet(0).Route, game.RouteColonist) assert.Contains(t, c.MustPlanet(0).Route, game.RouteCapital) assert.Contains(t, c.MustPlanet(2).Route, game.RouteEmpty) assert.NoError(t, g.RemoveRoute(Race_0.Name, "COL", 0)) assert.NotContains(t, c.MustPlanet(0).Route, game.RouteColonist) assert.Contains(t, c.MustPlanet(0).Route, game.RouteCapital) assert.NoError(t, g.RemoveRoute(Race_0.Name, "EMP", 2)) assert.NotContains(t, c.MustPlanet(2).Route, game.RouteEmpty) assert.ErrorContains(t, g.RemoveRoute("UnknownRace", "COL", 0), e.GenericErrorText(e.ErrInputUnknownRace)) assert.ErrorContains(t, g.RemoveRoute(Race_0.Name, "IND", 0), e.GenericErrorText(e.ErrInputCargoTypeInvalid)) assert.ErrorContains(t, g.RemoveRoute(Race_0.Name, "COL", 500), e.GenericErrorText(e.ErrInputEntityNotExists)) assert.ErrorContains(t, g.RemoveRoute(Race_0.Name, "COL", 1), e.GenericErrorText(e.ErrInputEntityNotOwned)) } func TestListRoutedSendGroupIds(t *testing.T) { c, g := newCache() // 1: idx = 0 / Ready to load assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 10)) // 2: idx = 1 / Has no cargo bay assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Gunship, R0_Planet_0_num, 1)) // 3: idx = 2 / In_Space assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 7)) c.ShipGroup(2).StateInSpace = &game.InSpace{ Origin: 2, Range: 31.337, } // 4: idx = 3 / loaded with COL assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 11)) c.ShipGroup(3).CargoType = game.CargoColonist.Ref() c.ShipGroup(3).Load = 1.234 // 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)) // 5: idx = 4 / Part of the Fleet assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 10)) assert.NoError(t, g.JoinShipGroupToFleet(Race_0.Name, "Fleet", 5, 0)) planet_0_groups := slices.Collect(c.ListRoutedSendGroupIds(0)) assert.Len(t, planet_0_groups, 1) for _, i := range planet_0_groups { sg := c.ShipGroup(i) st := c.ShipGroupShipClass(i) 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.F()) assert.Nil(t, sg.FleetID) } } func TestEnrouteGroups_SplitGroup(t *testing.T) { c, g := newCache() assert.NoError(t, g.SetRoute(Race_0.Name, "COL", R0_Planet_0_num, R0_Planet_2_num)) c.MustPlanet(R0_Planet_0_num).Colonists = 65 assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 5)) // 21.0 per Ship assert.Equal(t, 105., c.ShipGroup(0).CargoCapacity(c.ShipGroupShipClass(0))) c.SendRoutedGroups() 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.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.F()) assert.Equal(t, 0., c.MustPlanet(R0_Planet_0_num).Colonists.F()) } func TestEnrouteGroups_GroupSorting(t *testing.T) { c, g := newCache() assert.NoError(t, g.SetRoute(Race_0.Name, "COL", R0_Planet_0_num, R0_Planet_2_num)) c.MustPlanet(R0_Planet_0_num).Colonists = 100 // 0: idx = 1 assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 4)) // 21.0 per Ship assert.Equal(t, 84., c.ShipGroup(0).CargoCapacity(c.ShipGroupShipClass(0))) // 1: idx = 2 assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 5)) // 21.0 per Ship assert.Equal(t, 105., c.ShipGroup(1).CargoCapacity(c.ShipGroupShipClass(1))) c.SendRoutedGroups() assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 2) 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.F()) assert.Equal(t, 0., c.MustPlanet(R0_Planet_0_num).Colonists.F()) } func TestEnrouteGroups_LaunchOrder(t *testing.T) { c, g := newCache() assert.NoError(t, g.SetRoute(Race_0.Name, "COL", R0_Planet_0_num, R0_Planet_2_num)) assert.NoError(t, g.SetRoute(Race_0.Name, "CAP", R0_Planet_0_num, R0_Planet_2_num)) assert.NoError(t, g.SetRoute(Race_0.Name, "MAT", R0_Planet_0_num, R0_Planet_2_num)) assert.NoError(t, g.SetRoute(Race_0.Name, "EMP", R0_Planet_0_num, R1_Planet_1_num)) c.MustPlanet(R0_Planet_0_num).Colonists = 150 c.MustPlanet(R0_Planet_0_num).Capital = 100 c.MustPlanet(R0_Planet_0_num).Material = 20 // 0: idx = 1 (105 COL) -> // 3: idx = 4 ( 45 COL) assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 5)) assert.Equal(t, 105., c.ShipGroup(0).CargoCapacity(c.ShipGroupShipClass(0))) // 1: idx = 2 (In_Orbit) -> // 4: idx = 5 (20 MAT) assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 5)) assert.Equal(t, 105., c.ShipGroup(1).CargoCapacity(c.ShipGroupShipClass(1))) // 2: idx = 3 (100 CAP) assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 5)) assert.Equal(t, 105., c.ShipGroup(2).CargoCapacity(c.ShipGroupShipClass(2))) c.SendRoutedGroups() assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 5) // full load of COL 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.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) // rest of COL 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.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) // full load of CAP 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.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) // partial load of MAT 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.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) // empty / on_planet 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.F()) assert.Nil(t, c.ShipGroup(sgi).CargoType) assert.Equal(t, uint(1), c.ShipGroup(sgi).Number) } func TestListRoutedUnloadShipGroupIds(t *testing.T) { c, g := newCache() // 1: idx = 0 / Empty cargo assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 10)) c.ShipGroup(0).CargoType = game.CargoColonist.Ref() c.ShipGroup(0).Load = 0. // 2: idx = 1 / Has no cargo bay assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Gunship, R0_Planet_0_num, 1)) // 3: idx = 2 / In_Space assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 7)) c.ShipGroup(2).StateInSpace = &game.InSpace{ Origin: 2, Range: 31.337, } // 4: idx = 3 / loaded with COL assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 11)) c.ShipGroup(3).CargoType = game.CargoColonist.Ref() c.ShipGroup(3).Load = 1.234 // 5: idx = 4 / Part of the Fleet assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 10)) assert.NoError(t, g.JoinShipGroupToFleet(Race_0.Name, "Fleet", 5, 0)) assert.NoError(t, g.SetRoute(Race_0.Name, "COL", R0_Planet_0_num, R0_Planet_2_num)) for _, rt := range game.RouteTypeSet { groups := slices.Collect(c.ListRoutedUnloadShipGroupIds(R0_Planet_2_num, rt)) assert.Len(t, groups, 0, "route: %v", rt) groups = slices.Collect(c.ListRoutedUnloadShipGroupIds(R0_Planet_0_num, rt)) assert.Len(t, groups, 0, "route: %v", rt) } // double route from different planets - must not double group ids assert.NoError(t, g.SetRoute(Race_0.Name, "COL", R0_Planet_2_num, R0_Planet_0_num)) assert.NoError(t, g.SetRoute(Race_1.Name, "COL", R1_Planet_1_num, R0_Planet_0_num)) // 6: idx = 5 / loaded with CAP assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 11)) c.ShipGroup(5).CargoType = game.CargoCapital.Ref() c.ShipGroup(5).Load = 1.234 groups := slices.Collect(c.ListRoutedUnloadShipGroupIds(R0_Planet_0_num, game.RouteColonist)) assert.Len(t, groups, 1) for _, sgi := range groups { assert.Greater(t, c.ShipGroup(sgi).Load, 0.) assert.Equal(t, game.StateInOrbit, c.ShipGroup(sgi).State()) assert.Equal(t, R0_Planet_0_num, c.ShipGroup(sgi).Destination) } groups = slices.Collect(c.ListRoutedUnloadShipGroupIds(R0_Planet_0_num, game.RouteMaterial)) assert.Len(t, groups, 0) groups = slices.Collect(c.ListRoutedUnloadShipGroupIds(R0_Planet_0_num, game.RouteCapital)) assert.Len(t, groups, 0) } func TestMaxOrRandomLoadId(t *testing.T) { IDtoLoad := make(map[int]float64) assert.Panics(t, func() { controller.MaxOrRandomLoadId(IDtoLoad) }) IDtoLoad[1] = 100. assert.Panics(t, func() { controller.MaxOrRandomLoadId(IDtoLoad) }) IDtoLoad[5] = 100.001 assert.Equal(t, 5, controller.MaxOrRandomLoadId(IDtoLoad)) IDtoLoad[3] = 100. assert.NotContains(t, []int{1, 3}, controller.MaxOrRandomLoadId(IDtoLoad)) IDtoLoad[7] = 100.001 rndCount := make(map[int]int) for range 100 { id := controller.MaxOrRandomLoadId(IDtoLoad) assert.NotContains(t, []int{1, 3}, id) assert.Contains(t, []int{5, 7}, id) rndCount[id]++ } assert.Greater(t, rndCount[5], 10) assert.Greater(t, rndCount[7], 10) } func TestSelectColUnloadGroup(t *testing.T) { c, g := newCache() assert.NoError(t, g.SetRoute(Race_0.Name, "COL", R0_Planet_2_num, R0_Planet_0_num)) assert.NoError(t, g.SetRoute(Race_1.Name, "COL", R1_Planet_1_num, R0_Planet_0_num)) // 1: idx = 0 / Loaded COL assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 10)) c.ShipGroup(0).CargoType = game.CargoColonist.Ref() c.ShipGroup(0).Load = 7. // 2: idx = 1 / Loaded COL assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 5)) c.ShipGroup(1).CargoType = game.CargoColonist.Ref() c.ShipGroup(1).Load = 5. groups := slices.Collect(c.ListRoutedUnloadShipGroupIds(R0_Planet_0_num, game.RouteColonist)) assert.Len(t, groups, 2) unloadGroups := slices.Collect(c.SelectColUnloadGroup(groups)) assert.ElementsMatch(t, groups, unloadGroups) // 3: idx = 2 / Loaded COL - another race, winner assert.NoError(t, c.CreateShips(Race_1_idx, Race_1_Freighter, R1_Planet_1_num, 10)) c.ShipGroup(2).Destination = R0_Planet_0_num c.ShipGroup(2).CargoType = game.CargoColonist.Ref() c.ShipGroup(2).Load = 12.1 groups = slices.Collect(c.ListRoutedUnloadShipGroupIds(R0_Planet_0_num, game.RouteColonist)) assert.Len(t, groups, 3) unloadGroups = slices.Collect(c.SelectColUnloadGroup(groups)) assert.Equal(t, 2, unloadGroups[0]) } func TestTurnUnloadEnroutedGroups(t *testing.T) { c, g := newCache() assert.NoError(t, g.SetRoute(Race_0.Name, "MAT", R0_Planet_2_num, R0_Planet_0_num)) assert.NoError(t, g.SetRoute(Race_0.Name, "CAP", R0_Planet_2_num, R0_Planet_0_num)) assert.NoError(t, g.SetRoute(Race_1.Name, "COL", R1_Planet_1_num, R0_Planet_0_num)) assert.NoError(t, g.SetRoute(Race_1.Name, "COL", R1_Planet_1_num, Uninhabited_Planet_4_num)) // 1: idx = 0 / Loaded MAT assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 10)) c.ShipGroup(0).CargoType = game.CargoMaterial.Ref() c.ShipGroup(0).Load = 222. // 2: idx = 1 / Loaded CAP assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 10)) c.ShipGroup(1).CargoType = game.CargoCapital.Ref() c.ShipGroup(1).Load = 11. // 3: idx = 2 / Loaded COL - on empty planet assert.NoError(t, c.CreateShips(Race_1_idx, Race_1_Freighter, R1_Planet_1_num, 10)) c.ShipGroup(2).Destination = Uninhabited_Planet_4_num c.ShipGroup(2).CargoType = game.CargoColonist.Ref() c.ShipGroup(2).Load = 12.1 // 4: idx = 3 / Loaded COL - on inhabited planet assert.NoError(t, c.CreateShips(Race_1_idx, Race_1_Freighter, R1_Planet_1_num, 10)) c.ShipGroup(3).Destination = R0_Planet_0_num c.ShipGroup(3).CargoType = game.CargoColonist.Ref() c.ShipGroup(3).Load = 17.3 c.TurnUnloadEnroutedGroups() 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.True(t, c.MustPlanet(Uninhabited_Planet_4_num).OwnedBy(Race_1_ID)) assert.Equal(t, game.ProductionCapital, c.MustPlanet(Uninhabited_Planet_4_num).Production.Type) assert.Equal(t, 17.3, c.ShipGroup(3).Load.F()) } func TestRemoveUnreachableRoutes(t *testing.T) { c, g := newCache() assert.NoError(t, g.SetRoute(Race_0.Name, "MAT", R0_Planet_2_num, R0_Planet_0_num)) assert.NoError(t, g.SetRoute(Race_0.Name, "CAP", R0_Planet_2_num, R0_Planet_0_num)) assert.NoError(t, g.SetRoute(Race_1.Name, "COL", R1_Planet_1_num, R0_Planet_0_num)) assert.NoError(t, g.SetRoute(Race_1.Name, "CAP", R1_Planet_1_num, Uninhabited_Planet_4_num)) assert.Error(t, g.SetRoute(Race_0.Name, "COL", R0_Planet_2_num, Uninhabited_Planet_3_num)) c.MustPlanet(R0_Planet_2_num).Route[game.RouteColonist] = Uninhabited_Planet_3_num c.RemoveUnreachableRoutes() assert.NotContains(t, c.MustPlanet(R0_Planet_2_num).Route, game.RouteColonist) assert.Contains(t, c.MustPlanet(R0_Planet_2_num).Route, game.RouteMaterial) assert.Contains(t, c.MustPlanet(R0_Planet_2_num).Route, game.RouteCapital) assert.Contains(t, c.MustPlanet(R1_Planet_1_num).Route, game.RouteColonist) assert.Contains(t, c.MustPlanet(R1_Planet_1_num).Route, game.RouteCapital) }