chore: refactor structure
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
package generator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
func Generate(cfg ...func(*MapSetting)) (Map, error) {
|
||||
ms := DefaultMapSetting()
|
||||
for i := range cfg {
|
||||
cfg[i](&ms)
|
||||
}
|
||||
|
||||
size := ms.ExpectedSize()
|
||||
|
||||
m, err := NewMap(size, size, ms.Players)
|
||||
if err != nil {
|
||||
return Map{}, fmt.Errorf("%s: NewMap: %s", ms, err)
|
||||
}
|
||||
|
||||
freePlanets := ms.NobodysPlanets()
|
||||
|
||||
createPlanets := func(pc PlanetClass, ps PlanetSetting) error {
|
||||
return m.CreatePlanets(pc, ps.Number(freePlanets), float64(ps.MinDistanceHW), RandIFn(ps.MinSize, ps.MaxSize), RandIFn(ps.MinResource, ps.MaxResource))
|
||||
}
|
||||
|
||||
// 1. Place Giant planets
|
||||
if err := createPlanets(PlanetClassGiant, ms.GiantPlanets); err != nil {
|
||||
return Map{}, fmt.Errorf("%s: create giant planets: %s", ms, err)
|
||||
}
|
||||
|
||||
// 2. Place Big planets
|
||||
if err := createPlanets(PlanetClassBig, ms.BigPlanets); err != nil {
|
||||
return Map{}, fmt.Errorf("%s: create big planets: %s", ms, err)
|
||||
}
|
||||
|
||||
// 3. Place players' Home Worlds
|
||||
for player := 0; player < int(ms.Players); player++ {
|
||||
hwCoord, err := m.NewCoordinate(float64(ms.HWMinDistance))
|
||||
if err != nil {
|
||||
return Map{}, fmt.Errorf("%s: hw new_coordinate: %s", ms, err)
|
||||
}
|
||||
hwPlanet := NewPlanet(PlanetClassHW, hwCoord, ms.HWSize, ms.HWResources)
|
||||
m.HomePlanets[player] = PlanetarySystem{HW: hwPlanet, DW: make([]Planet, ms.DWCount)}
|
||||
for dw := 0; dw < int(ms.DWCount); dw++ {
|
||||
p := rand.Float64()*(float64(ms.DWMaxDistance)-float64(ms.DWMinDistance)) + float64(ms.DWMinDistance)
|
||||
phi := rand.Float64() * 360
|
||||
x := p * math.Cos(phi)
|
||||
y := p * math.Sin(phi)
|
||||
dwPlanet := NewPlanet(PlanetClassDW, Coordinate{hwCoord.X + x, hwCoord.Y + y}, ms.DWSize, ms.DWResources)
|
||||
m.HomePlanets[player].DW[dw] = dwPlanet
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Clear plotter and set dead zones around existing planets
|
||||
m.plotter.Clear()
|
||||
for i := range m.HomePlanets {
|
||||
m.plotter.MarkDeadZone(m.HomePlanets[i].HW.Position.X, m.HomePlanets[i].HW.Position.Y, ms.OthersMinDistance)
|
||||
for j := range m.HomePlanets[i].DW {
|
||||
m.plotter.MarkDeadZone(m.HomePlanets[i].DW[j].Position.X, m.HomePlanets[i].DW[j].Position.Y, ms.OthersMinDistance)
|
||||
}
|
||||
}
|
||||
for i := range m.FreePlanets {
|
||||
m.plotter.MarkDeadZone(m.FreePlanets[i].Position.X, m.FreePlanets[i].Position.Y, ms.OthersMinDistance)
|
||||
}
|
||||
|
||||
// 5. Place Normal planets
|
||||
if err := createPlanets(PlanetClassNormal, ms.NormalPlanets); err != nil {
|
||||
return Map{}, fmt.Errorf("%s: create normal planets: %s", ms, err)
|
||||
}
|
||||
|
||||
// 6. Place Rich planets
|
||||
if err := createPlanets(PlanetClassRich, ms.RichPlanets); err != nil {
|
||||
return Map{}, fmt.Errorf("%s: create rich planets: %s", ms, err)
|
||||
}
|
||||
|
||||
// 7. Place Asteroids
|
||||
if err := createPlanets(PlanetClassAsterioid, ms.Asterioids); err != nil {
|
||||
return Map{}, fmt.Errorf("%s: create asteroids: %s", ms, err)
|
||||
}
|
||||
|
||||
return *m, nil
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package generator_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/iliadenisov/galaxy/internal/generator"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGenerator(t *testing.T) {
|
||||
maxPlayers := 20
|
||||
for players := 10; players <= maxPlayers; players++ {
|
||||
t.Run(fmt.Sprintf("%d_players", players), func(t *testing.T) {
|
||||
var s generator.MapSetting
|
||||
m, err := generator.Generate(func(ms *generator.MapSetting) {
|
||||
ms.Players = uint32(players)
|
||||
s = *ms
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("generate: %s", err)
|
||||
return
|
||||
}
|
||||
assert.Equal(t, players, len(m.HomePlanets), "hw-s count")
|
||||
for hw := range m.HomePlanets {
|
||||
assert.Equal(t, s.HWSize, m.HomePlanets[hw].HW.Size, "hw #%d: size", hw)
|
||||
assert.Equal(t, s.HWResources, m.HomePlanets[hw].HW.Resources, "hw #%d: resources", hw)
|
||||
assert.Equal(t, int(s.DWCount), len(m.HomePlanets[hw].DW), "hw #%d: dw-s count", hw)
|
||||
for dw := range m.HomePlanets[hw].DW {
|
||||
assert.Equal(t, s.DWSize, m.HomePlanets[hw].DW[dw].Size, "hw #%d dw #%d: size", hw, dw)
|
||||
assert.Equal(t, s.DWResources, m.HomePlanets[hw].DW[dw].Resources, "hw #%d dw #%d: resources", hw, dw)
|
||||
d := m.ShortDistance(m.HomePlanets[hw].HW.Position, m.HomePlanets[hw].DW[dw].Position)
|
||||
assert.LessOrEqualf(t, float64(s.DWMinDistance), d, "distance: HW[%.04f,%04f] <-> DW[%.04f,%04f]",
|
||||
m.HomePlanets[hw].HW.Position.X, m.HomePlanets[hw].HW.Position.Y,
|
||||
m.HomePlanets[hw].DW[dw].Position.X, m.HomePlanets[hw].DW[dw].Position.Y)
|
||||
assert.GreaterOrEqualf(t, float64(s.DWMaxDistance), d, "distance: HW[%.04f,%04f] <-> DW[%.04f,%04f]",
|
||||
m.HomePlanets[hw].HW.Position.X, m.HomePlanets[hw].HW.Position.Y,
|
||||
m.HomePlanets[hw].DW[dw].Position.X, m.HomePlanets[hw].DW[dw].Position.Y)
|
||||
}
|
||||
}
|
||||
assert.LessOrEqualf(t, int(s.NobodysPlanets()), len(m.FreePlanets), "free planets clount")
|
||||
freePlanetCount := make(map[generator.PlanetClass]int)
|
||||
for fp := range m.FreePlanets {
|
||||
ps := planetSettings(t, m.FreePlanets[fp].PlanetClass, s)
|
||||
testPlanetParameters(t, ps, m.FreePlanets[fp])
|
||||
if v, ok := freePlanetCount[m.FreePlanets[fp].PlanetClass]; !ok {
|
||||
freePlanetCount[m.FreePlanets[fp].PlanetClass] = 1
|
||||
} else {
|
||||
freePlanetCount[m.FreePlanets[fp].PlanetClass] = v + 1
|
||||
}
|
||||
if ps.MinDistanceHW > 0 {
|
||||
for hw := range m.HomePlanets {
|
||||
d := m.ShortDistance(m.HomePlanets[hw].HW.Position, m.FreePlanets[fp].Position)
|
||||
// FIXME:
|
||||
// Error: "20" is not less than or equal to "19.98697122994701"
|
||||
// Test: TestGenerator/24_players
|
||||
// Messages: distance: HW[44.4883,136.985727] <-> %!s(generator.PlanetClass=2)[38.9977,156.203728]
|
||||
//
|
||||
// Error: "10" is not less than or equal to "9.985592188977868"
|
||||
// Test: TestGenerator/33_players
|
||||
// Messages: distance: HW[231.7975,76.996315] <-> planet_class=3[237.7334,85.026044]
|
||||
assert.LessOrEqualf(t, float64(ps.MinDistanceHW), d, "distance: HW[%.04f,%04f] <-> planet_class=%v[%.04f,%04f]",
|
||||
m.HomePlanets[hw].HW.Position.X, m.HomePlanets[hw].HW.Position.Y,
|
||||
m.FreePlanets[fp].PlanetClass,
|
||||
m.FreePlanets[fp].Position.X, m.FreePlanets[fp].Position.Y)
|
||||
}
|
||||
}
|
||||
}
|
||||
for pc, num := range freePlanetCount {
|
||||
ps := planetSettings(t, pc, s)
|
||||
maxNum := ps.Number(s.NobodysPlanets())
|
||||
assert.Equalf(t, num, maxNum, "planet_class=%v ratio=%f of total %d", pc, ps.Ratio, s.NobodysPlanets())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func testPlanetParameters(t *testing.T, s generator.PlanetSetting, p generator.Planet) {
|
||||
assert.LessOrEqualf(t, s.MinResource, p.Resources, "planet class=%s min resources", p.PlanetClass)
|
||||
assert.GreaterOrEqualf(t, s.MaxResource, p.Resources, "planet class=%s max resources", p.PlanetClass)
|
||||
assert.LessOrEqualf(t, s.MinSize, p.Size, "planet class=%s min size", p.PlanetClass)
|
||||
assert.GreaterOrEqualf(t, s.MaxSize, p.Size, "planet class=%s max size", p.PlanetClass)
|
||||
}
|
||||
|
||||
func planetSettings(t *testing.T, pc generator.PlanetClass, s generator.MapSetting) generator.PlanetSetting {
|
||||
switch pc {
|
||||
case generator.PlanetClassGiant:
|
||||
return s.GiantPlanets
|
||||
case generator.PlanetClassBig:
|
||||
return s.BigPlanets
|
||||
case generator.PlanetClassNormal:
|
||||
return s.NormalPlanets
|
||||
case generator.PlanetClassRich:
|
||||
return s.RichPlanets
|
||||
case generator.PlanetClassAsterioid:
|
||||
return s.Asterioids
|
||||
default:
|
||||
assert.FailNow(t, "unexpected planet class: %s", pc)
|
||||
return generator.PlanetSetting{}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGenerator(b *testing.B) {
|
||||
i := 0
|
||||
for b.Loop() {
|
||||
i++
|
||||
b.Run(fmt.Sprintf("instance #%02d", i), func(b *testing.B) {
|
||||
_, err := generator.Generate()
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package generator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
|
||||
"github.com/iliadenisov/galaxy/internal/generator/plotter"
|
||||
)
|
||||
|
||||
type Map struct {
|
||||
Width uint32
|
||||
Height uint32
|
||||
HomePlanets []PlanetarySystem
|
||||
FreePlanets []Planet
|
||||
plotter plotter.Plotter
|
||||
}
|
||||
|
||||
type Coordinate struct {
|
||||
X, Y float64
|
||||
}
|
||||
|
||||
func NewMap(width, height, players uint32) (*Map, error) {
|
||||
p, err := plotter.NewPlotter(width, height, defaultFactor)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("NewPlotter: %s", err)
|
||||
}
|
||||
return &Map{
|
||||
Width: width,
|
||||
Height: height,
|
||||
HomePlanets: make([]PlanetarySystem, players),
|
||||
plotter: p,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *Map) CreatePlanets(pc PlanetClass, num int, deadZoneRadius float64, size, resources func() float64) error {
|
||||
for range num {
|
||||
coord, err := m.NewCoordinate(deadZoneRadius)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
planet := NewPlanet(pc, coord, size(), resources())
|
||||
m.AddPlanet(planet)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Map) AddPlanet(planet Planet) {
|
||||
m.FreePlanets = append(m.FreePlanets, planet)
|
||||
}
|
||||
|
||||
func (m Map) NewCoordinate(deadZoneRaduis float64) (Coordinate, error) {
|
||||
if x, y, err := m.plotter.RandomFreePoint(deadZoneRaduis); err != nil {
|
||||
return Coordinate{}, fmt.Errorf("NewCoordinate: RandomFreePoint: %s", err)
|
||||
} else {
|
||||
return Coordinate{X: x, Y: y}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (m Map) ShortDistance(from, to Coordinate) float64 {
|
||||
dx := math.Abs(to.X - from.X)
|
||||
dy := math.Abs(to.Y - from.Y)
|
||||
if dx > float64(m.Width/2) {
|
||||
dx = float64(m.Width) - dx
|
||||
}
|
||||
if dy > float64(m.Height/2) {
|
||||
dy = float64(m.Height) - dy
|
||||
}
|
||||
return math.Sqrt(math.Pow(dx, 2) + math.Pow(dy, 2))
|
||||
}
|
||||
|
||||
// RandI returns a random float64 value between min and max
|
||||
func RandI(min, max float64) float64 {
|
||||
return min + rand.Float64()*(max-min)
|
||||
}
|
||||
|
||||
// RandIFn is a wrapper for the [RandI] func
|
||||
func RandIFn(min, max float64) func() float64 {
|
||||
return func() float64 {
|
||||
return RandI(min, max)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package generator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/iliadenisov/galaxy/internal/number"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestShortDistance(t *testing.T) {
|
||||
for i, tc := range []struct {
|
||||
w, h uint32
|
||||
x1, y1, x2, y2, d float64
|
||||
}{
|
||||
{10, 10, 0, 0, 5, 5, 7.071},
|
||||
{10, 10, 0, 0, 5.01, 5.01, 7.057},
|
||||
{10, 10, 2, 2, 8, 2, 4.},
|
||||
{10, 10, 8, 7, 1, 7, 3.},
|
||||
} {
|
||||
t.Run(fmt.Sprint(i), func(t *testing.T) {
|
||||
m := Map{Width: tc.w, Height: tc.h}
|
||||
d := m.ShortDistance(Coordinate{tc.x1, tc.y1}, Coordinate{tc.x2, tc.y2})
|
||||
assert.Equal(t, tc.d, number.Fixed3(d))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package generator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
type PlanetClass string
|
||||
|
||||
const (
|
||||
PlanetClassHW PlanetClass = "HW"
|
||||
PlanetClassDW PlanetClass = "DW"
|
||||
PlanetClassGiant PlanetClass = "Giant"
|
||||
PlanetClassBig PlanetClass = "Big"
|
||||
PlanetClassNormal PlanetClass = "Normal"
|
||||
PlanetClassRich PlanetClass = "Rich"
|
||||
PlanetClassAsterioid PlanetClass = "Asteroid"
|
||||
)
|
||||
|
||||
type Planet struct {
|
||||
PlanetClass PlanetClass
|
||||
Position Coordinate
|
||||
Size float64
|
||||
Resources float64 // Сырьё
|
||||
}
|
||||
|
||||
type PlanetarySystem struct {
|
||||
HW Planet
|
||||
DW []Planet
|
||||
}
|
||||
|
||||
func (p Planet) RandomName() string {
|
||||
return fmt.Sprintf("%s-%04d-%04d", p.PlanetClass, rand.Intn(1000), rand.Intn(1000))
|
||||
}
|
||||
|
||||
func NewPlanet(pc PlanetClass, c Coordinate, size, resources float64) Planet {
|
||||
return Planet{
|
||||
PlanetClass: pc,
|
||||
Position: c,
|
||||
Size: size,
|
||||
Resources: resources,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package generator_test
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
g "github.com/iliadenisov/galaxy/internal/generator"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestPlanetRandomName(t *testing.T) {
|
||||
re, err := regexp.Compile(`^([a-zA-Z]+)-(\d{4})-(\d{4})$`)
|
||||
assert.NoError(t, err)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, pc := range []g.PlanetClass{g.PlanetClassHW, g.PlanetClassDW, g.PlanetClassGiant, g.PlanetClassBig, g.PlanetClassNormal, g.PlanetClassRich, g.PlanetClassAsterioid} {
|
||||
t.Run(string(pc), func(t *testing.T) {
|
||||
name := g.NewPlanet(pc, g.Coordinate{0, 0}, 0, 0).RandomName()
|
||||
g := re.FindStringSubmatch(name)
|
||||
assert.NotNilf(t, g, "cannot parse: %q", name)
|
||||
if g == nil {
|
||||
return
|
||||
}
|
||||
assert.Equalf(t, 4, len(g), "regexp groups")
|
||||
assert.Equal(t, string(pc), g[1])
|
||||
assert.NotEqual(t, g[2], g[3])
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package plotter
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
|
||||
"github.com/iliadenisov/galaxy/internal/bitmap"
|
||||
)
|
||||
|
||||
type Plotter struct {
|
||||
factor float64
|
||||
clearFn func()
|
||||
circleFn func(x, y int, r float64)
|
||||
freeCountFn func() int
|
||||
freeNumberToCoordFn func(int) (int, int, error)
|
||||
}
|
||||
|
||||
func NewPlotter(width, height uint32, factor float64) (Plotter, error) {
|
||||
return NewBitmapPlotter(NewBitmap(width, height, factor), factor)
|
||||
}
|
||||
|
||||
func NewBitmap(width, height uint32, factor float64) bitmap.Bitmap {
|
||||
return bitmap.NewBitmap(AsPlotterSize(width, height, factor))
|
||||
}
|
||||
|
||||
func NewBitmapPlotter(bm bitmap.Bitmap, factor float64) (Plotter, error) {
|
||||
if factor > 1 || factor <= 0 {
|
||||
return Plotter{}, fmt.Errorf("factor should be: 0 > F <= 1")
|
||||
}
|
||||
return Plotter{
|
||||
factor: factor,
|
||||
clearFn: bm.Clear,
|
||||
circleFn: func(x, y int, r float64) { bm.Circle(x, y, r, true) },
|
||||
freeCountFn: bm.FreeCount,
|
||||
freeNumberToCoordFn: bm.GetFreeN,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p Plotter) RandomFreePoint(deadZoneRaduis float64) (float64, float64, error) {
|
||||
fsCount := p.freeCountFn()
|
||||
if fsCount == 0 {
|
||||
return 0, 0, errors.New("RandomFreePoint: no free space left")
|
||||
}
|
||||
next := rand.Intn(fsCount)
|
||||
x, y, err := p.freeNumberToCoordFn(next)
|
||||
if err != nil {
|
||||
return 0, 0, fmt.Errorf("RandomFreePoint: freeNumberToCoordFn: %s", err)
|
||||
}
|
||||
if deadZoneRaduis > 0 {
|
||||
p.plotDeadZone(x, y, deadZoneRaduis)
|
||||
}
|
||||
planetX := float64(x)*p.factor + rand.Float64()*p.factor
|
||||
planetY := float64(y)*p.factor + rand.Float64()*p.factor
|
||||
return planetX, planetY, nil
|
||||
}
|
||||
|
||||
func (p Plotter) MarkDeadZone(x, y float64, radius float64) {
|
||||
p.plotDeadZone(int(x/p.factor), int(y/p.factor), radius)
|
||||
}
|
||||
|
||||
func (p Plotter) plotDeadZone(x, y int, radius float64) {
|
||||
p.circleFn(x, y, radius/p.factor)
|
||||
}
|
||||
|
||||
func (p Plotter) Clear() { p.clearFn() }
|
||||
|
||||
func AsPlotterSize(width, height uint32, factor float64) (uint32, uint32) {
|
||||
if factor > 1 || factor <= 0 {
|
||||
return width, height
|
||||
}
|
||||
return uint32(math.Ceil(float64(width) / float64(factor))), uint32(math.Ceil(float64(height) / float64(factor)))
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package plotter_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/iliadenisov/galaxy/internal/generator/plotter"
|
||||
)
|
||||
|
||||
func TestNewPlotter(t *testing.T) {
|
||||
_, err := plotter.NewPlotter(10, 10, 0)
|
||||
if err == nil {
|
||||
t.Error("expect: error when factor=0")
|
||||
}
|
||||
_, err = plotter.NewPlotter(10, 10, -0.01)
|
||||
if err == nil {
|
||||
t.Error("expect: error when factor<0")
|
||||
}
|
||||
_, err = plotter.NewPlotter(10, 10, 1.001)
|
||||
if err == nil {
|
||||
t.Error("expect: error when factor>1")
|
||||
}
|
||||
_, err = plotter.NewPlotter(10, 10, 1)
|
||||
if err != nil {
|
||||
t.Error("expect: no error when factor=1")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAsPlotterSize(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
w, h uint32
|
||||
f float64
|
||||
ew, eh uint32
|
||||
}{
|
||||
{10, 10, 0, 10, 10},
|
||||
{10, 10, -1, 10, 10},
|
||||
{10, 10, 1.1, 10, 10},
|
||||
{10, 20, 0.5, 20, 40},
|
||||
{30, 60, 0.3, 100, 200},
|
||||
{10, 20, 0.25, 40, 80},
|
||||
} {
|
||||
w, h := plotter.AsPlotterSize(tc.w, tc.h, tc.f)
|
||||
if tc.ew != w || tc.eh != h {
|
||||
t.Errorf("expect: w=%d h=%d, got: w=%d h=%d", tc.ew, tc.eh, w, h)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandomFreePoint(t *testing.T) {
|
||||
var factor float64 = 0.25
|
||||
var w, h uint32 = 20, 20
|
||||
bm := plotter.NewBitmap(w, h, factor)
|
||||
p, err := plotter.NewBitmapPlotter(bm, factor) // 80x80
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
x, y, err := p.RandomFreePoint(3)
|
||||
if err != nil {
|
||||
t.Errorf("expect: no error getting random point, got: %s", err)
|
||||
}
|
||||
if x > float64(w) || y > float64(w) {
|
||||
t.Errorf("expect: point coordinates within map size %dx%d, got: x=%f y=%f", w, h, x, y)
|
||||
}
|
||||
|
||||
_, _, err = p.RandomFreePoint(0)
|
||||
if err != nil {
|
||||
t.Errorf("expect: no error when radius is zero, got: %s", err)
|
||||
}
|
||||
|
||||
_, _, err = p.RandomFreePoint(float64(w + h)) // guaranteed to mark whole area dead zone
|
||||
if err != nil {
|
||||
t.Errorf("expect: no error getting random point, got: %s", err)
|
||||
}
|
||||
|
||||
_, _, err = p.RandomFreePoint(1)
|
||||
if err == nil {
|
||||
t.Error("expect: error when no free space left, got: none")
|
||||
}
|
||||
|
||||
p.Clear()
|
||||
|
||||
_, _, err = p.RandomFreePoint(10)
|
||||
if err != nil {
|
||||
t.Errorf("expect: no error getting random point after clearing, got: %s", err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package generator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
|
||||
const defaultFactor float64 = 0.1
|
||||
|
||||
type MapSetting struct {
|
||||
Players uint32
|
||||
HWSize float64
|
||||
HWResources float64
|
||||
HWMinDistance uint32
|
||||
DWCount uint32
|
||||
DWSize float64
|
||||
DWResources float64
|
||||
DWMinDistance uint32
|
||||
DWMaxDistance uint32
|
||||
|
||||
GiantPlanets PlanetSetting
|
||||
BigPlanets PlanetSetting
|
||||
OthersMinDistance float64
|
||||
NormalPlanets PlanetSetting
|
||||
RichPlanets PlanetSetting
|
||||
Asterioids PlanetSetting
|
||||
}
|
||||
|
||||
func (ms MapSetting) String() string {
|
||||
return fmt.Sprintf("MapSetting[players=%d HWMinDistance=%d Size=%d]", ms.Players, ms.HWMinDistance, ms.ExpectedSize())
|
||||
}
|
||||
|
||||
func (ms MapSetting) ExpectedSize() uint32 {
|
||||
return uint32(math.Sqrt(float64(ms.Players)) * float64(ms.HWMinDistance) * 1.5)
|
||||
}
|
||||
|
||||
func (ms MapSetting) TotalPlanets() uint32 {
|
||||
return ms.Players * 10
|
||||
}
|
||||
|
||||
func (ms MapSetting) NobodysPlanets() uint32 {
|
||||
return ms.TotalPlanets() - ms.Players*(ms.DWCount+1)
|
||||
}
|
||||
|
||||
type PlanetSetting struct {
|
||||
MinDistanceHW uint32
|
||||
MinSize float64
|
||||
MaxSize float64
|
||||
MinResource float64
|
||||
MaxResource float64
|
||||
Ratio float64 // The proportion of the total number of free planets in the galaxy
|
||||
}
|
||||
|
||||
// Number of planets need to be placed within freePlanets amount
|
||||
func (ps PlanetSetting) Number(freePlanets uint32) int {
|
||||
return int(math.Ceil(float64(freePlanets) * ps.Ratio))
|
||||
}
|
||||
|
||||
func DefaultMapSetting() MapSetting {
|
||||
return MapSetting{
|
||||
Players: 25,
|
||||
HWSize: 1000,
|
||||
HWResources: 10,
|
||||
HWMinDistance: 30,
|
||||
DWCount: 2,
|
||||
DWSize: 500,
|
||||
DWResources: 10,
|
||||
DWMinDistance: 5,
|
||||
DWMaxDistance: 15,
|
||||
GiantPlanets: PlanetSetting{
|
||||
MinDistanceHW: 20,
|
||||
MinSize: 1500,
|
||||
MaxSize: 2500,
|
||||
MinResource: 0,
|
||||
MaxResource: 3,
|
||||
Ratio: 0.06,
|
||||
},
|
||||
BigPlanets: PlanetSetting{
|
||||
MinDistanceHW: 10,
|
||||
MinSize: 1000,
|
||||
MaxSize: 2000,
|
||||
MinResource: 1,
|
||||
MaxResource: 10,
|
||||
Ratio: 0.18,
|
||||
},
|
||||
OthersMinDistance: defaultFactor, // min. is 1 pixel on the plotter
|
||||
NormalPlanets: PlanetSetting{
|
||||
MinDistanceHW: 0,
|
||||
MinSize: 0,
|
||||
MaxSize: 1000,
|
||||
MinResource: 0,
|
||||
MaxResource: 10,
|
||||
Ratio: 0.5,
|
||||
},
|
||||
RichPlanets: PlanetSetting{
|
||||
MinDistanceHW: 0,
|
||||
MinSize: 0,
|
||||
MaxSize: 500,
|
||||
MinResource: 5,
|
||||
MaxResource: 25,
|
||||
Ratio: 0.18,
|
||||
},
|
||||
Asterioids: PlanetSetting{
|
||||
MinDistanceHW: 0,
|
||||
MinSize: 0,
|
||||
MaxSize: 0,
|
||||
MinResource: 0,
|
||||
MaxResource: 0,
|
||||
Ratio: 0.08,
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user