package game import ( "fmt" "math" "github.com/google/uuid" "github.com/iliadenisov/galaxy/pkg/generator" "github.com/iliadenisov/galaxy/pkg/model/game" "github.com/iliadenisov/galaxy/pkg/number" ) type Repo interface { Persist(game.Game) error } func NewGame(r Repo, races []string) (uuid.UUID, error) { id, err := uuid.NewRandom() if err != nil { return uuid.Nil, fmt.Errorf("generate uuid: %s", err) } m, err := generator.Generate(func(ms *generator.MapSetting) { ms.Players = uint32(len(races)) }) if err != nil { return uuid.Nil, fmt.Errorf("generate map: %s", err) } if len(races) != len(m.HomePlanets) { return uuid.Nil, fmt.Errorf("generate map: wrong number of home planets: %d, expected: %d ", len(m.HomePlanets), len(races)) } g := &game.Game{ ID: id, Race: make([]game.Race, len(races)), } gameMap := &game.Map{ Width: m.Width, Height: m.Height, Planet: make([]game.Planet, 0), } for hw := range races { g.Race[hw] = game.Race{ Name: races[hw], Votes: 1, // TODO: check with rules VoteFor: races[hw], Drive: 1, Weapons: 1, Shields: 1, Cargo: 1, } gameMap.Planet = append(gameMap.Planet, game.Planet{ Owner: races[hw], X: m.HomePlanets[hw].HW.Position.X, Y: m.HomePlanets[hw].HW.Position.Y, Size: m.HomePlanets[hw].HW.Size, Resources: m.HomePlanets[hw].HW.Resources, Production: game.ProductionCapital.AsType(""), // TODO: check default production }) for dw := range m.HomePlanets[hw].DW { gameMap.Planet = append(gameMap.Planet, game.Planet{ X: m.HomePlanets[hw].DW[dw].Position.X, Y: m.HomePlanets[hw].DW[dw].Position.Y, Size: m.HomePlanets[hw].DW[dw].Size, Resources: m.HomePlanets[hw].DW[dw].Resources, Production: game.ProductionNone.AsType(""), }) } } for i := range m.FreePlanets { gameMap.Planet = append(gameMap.Planet, game.Planet{ X: m.FreePlanets[i].Position.X, Y: m.FreePlanets[i].Position.Y, Size: m.FreePlanets[i].Size, Resources: m.FreePlanets[i].Resources, Production: game.ProductionNone.AsType(""), }) } g.Map = *gameMap if err := r.Persist(*g); err != nil { return uuid.Nil, fmt.Errorf("persist: %s", err) } return g.ID, nil } func (r Race) FlightDistance() float64 { return r.Drive * 40 } func (r Race) VisibilityDistance() float64 { return r.Drive * 30 } // Производственный потенциал (I) // промышленность * 0.75 + население * 0.25 func (p Planet) ProductionCapacity() float64 { return p.Industry*0.75 + p.Population*0.25 } // Производство промышленности // TODO: test on real values func (p *Planet) IncreaseIndustry() { prod := p.ProductionCapacity() / 5 industryIncrement := math.Min(prod, p.Material) p.Industry += industryIncrement if p.Industry > p.Population { p.Industry = p.Population p.Capital += p.Population - p.Industry } } // Производство материалов // TODO: test on real values func (p *Planet) IncreaseMaterial() { p.Material += p.ProductionCapacity() * p.Industry } // Автоматическое увеличение населения на каждом ходу func (p *Planet) IncreasePopulation() { p.Population *= 1.08 var extraPopulation = p.Size - p.Population if extraPopulation > 0 { p.Colonists += extraPopulation / 8 p.Population -= extraPopulation } } // TODO: test on real values func (st ShipType) EmptyMass() float64 { shipMass := st.DriveMass() + st.ShieldsMass() + st.CargoMass() + st.WeaponsMass() return shipMass } func (st ShipType) DriveMass() float64 { return st.Drive } func (st ShipType) ShieldsMass() float64 { return st.Shields } func (st ShipType) CargoMass() float64 { return st.Cargo } func (st ShipType) WeaponsMass() float64 { return float64(st.Armament)*(st.Weapons/2) + st.Weapons/2 } // Грузоподъёмность func (sg ShipGroup) CargoCapacity() float64 { return sg.Drive * (sg.Type.Cargo + (sg.Type.Cargo*sg.Type.Cargo)/20) } // "Масса перевозимого груза" func (sg ShipGroup) CarryingMass() float64 { return sg.Load / sg.Cargo } func (sg ShipGroup) FullMass() float64 { return sg.Type.EmptyMass() + sg.CarryingMass() } // "Эффективность двигателя" // равна мощности Двигателей умноженной на текущий технологический уровень блока Двигателей func (sg ShipGroup) DriveEffective() float64 { return sg.Type.Drive * sg.Drive } // TODO: test this func (sg ShipGroup) Speed() float64 { return sg.DriveEffective() * 20 / sg.FullMass() } func (sg ShipGroup) UpgradeDriveCost(drive float64) float64 { return (1 - sg.Drive/drive) * 10 * sg.Type.Drive } // TODO: test on other values func (sg ShipGroup) UpgradeWeaponsCost(weapons float64) float64 { return (1 - sg.Weapons/weapons) * 10 * sg.Type.WeaponsMass() } func (sg ShipGroup) UpgradeShieldsCost(shields float64) float64 { return (1 - sg.Shields/shields) * 10 * sg.Type.Shields } func (sg ShipGroup) UpgradeCargoCost(cargo float64) float64 { return (1 - sg.Cargo/cargo) * 10 * sg.Type.Cargo } // Мощность бомбардировки // TODO: maybe rounding must be done only for display? func (sg ShipGroup) BombingPower() float64 { // return math.Sqrt(sg.Type.Weapons * sg.Weapons) result := (math.Sqrt(sg.Type.Weapons*sg.Weapons)/10. + 1.) * sg.Type.Weapons * sg.Weapons * float64(sg.Type.Armament) * float64(sg.Number) return number.Fixed3(result) } // TODO: test this func (fl Fleet) Speed() float64 { result := math.MaxFloat64 for _, sg := range fl.ShipGroups { if sg.Speed() < result { result = sg.Speed() } } return result }