feat: moge groups in hyperspace
This commit is contained in:
@@ -70,3 +70,7 @@ func (c *Cache) RaceTechLevel(ri int, t game.Tech, v float64) {
|
|||||||
func (c *Cache) ListRouteEligibleGroupIds(pn uint) iter.Seq[int] {
|
func (c *Cache) ListRouteEligibleGroupIds(pn uint) iter.Seq[int] {
|
||||||
return c.listRouteEligibleGroupIds(pn)
|
return c.listRouteEligibleGroupIds(pn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Cache) ListMoveableGroupIds() iter.Seq[int] {
|
||||||
|
return c.listMoveableGroupIds()
|
||||||
|
}
|
||||||
|
|||||||
@@ -94,9 +94,9 @@ func newGame() *game.Game {
|
|||||||
Width: 1000,
|
Width: 1000,
|
||||||
Height: 1000,
|
Height: 1000,
|
||||||
Planet: []game.Planet{
|
Planet: []game.Planet{
|
||||||
controller.NewPlanet(R0_Planet_0_num, "Planet_0", Race_0.ID, 0, 0, 100, 100, 100, 0, game.ProductionNone.AsType(uuid.Nil)),
|
controller.NewPlanet(R0_Planet_0_num, "Planet_0", Race_0.ID, 1, 1, 100, 100, 100, 0, game.ProductionNone.AsType(uuid.Nil)),
|
||||||
controller.NewPlanet(R1_Planet_1_num, "Planet_1", Race_1.ID, 1, 1, 100, 0, 0, 0, game.ProductionNone.AsType(uuid.Nil)),
|
controller.NewPlanet(R1_Planet_1_num, "Planet_1", Race_1.ID, 2, 2, 100, 0, 0, 0, game.ProductionNone.AsType(uuid.Nil)),
|
||||||
controller.NewPlanet(R0_Planet_2_num, "Planet_2", Race_0.ID, 2, 2, 100, 0, 0, 0, game.ProductionNone.AsType(uuid.Nil)),
|
controller.NewPlanet(R0_Planet_2_num, "Planet_2", Race_0.ID, 3, 3, 100, 0, 0, 0, game.ProductionNone.AsType(uuid.Nil)),
|
||||||
controller.NewPlanet(3, "Planet_3", uuid.Nil, 500, 500, 100, 0, 0, 0, game.ProductionNone.AsType(uuid.Nil)),
|
controller.NewPlanet(3, "Planet_3", uuid.Nil, 500, 500, 100, 0, 0, 0, game.ProductionNone.AsType(uuid.Nil)),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -250,6 +250,25 @@ func (c *Cache) FleetGroups(ri, fi int) iter.Seq[*game.ShipGroup] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Cache) fleetGroupIds(ri, fi int) iter.Seq[int] {
|
||||||
|
c.validateRaceIndex(ri)
|
||||||
|
c.validateFleetIndex(fi)
|
||||||
|
return func(yield func(int) bool) {
|
||||||
|
for i := range c.ShipGroupsIndex() {
|
||||||
|
sg := c.ShipGroup(i)
|
||||||
|
if c.g.Race[ri].ID != sg.OwnerID {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if sg.FleetID == nil || c.MustFleetIndex(*sg.FleetID) != fi {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !yield(i) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Cache) listFleets(ri int) iter.Seq[*game.Fleet] {
|
func (c *Cache) listFleets(ri int) iter.Seq[*game.Fleet] {
|
||||||
c.validateRaceIndex(ri)
|
c.validateRaceIndex(ri)
|
||||||
return func(yield func(*game.Fleet) bool) {
|
return func(yield func(*game.Fleet) bool) {
|
||||||
|
|||||||
@@ -15,9 +15,18 @@ func MakeTurn(c *Controller, r Repo, g *game.Game) error {
|
|||||||
// 02. Враждующие корабли вступают в схватку.
|
// 02. Враждующие корабли вступают в схватку.
|
||||||
battles := ProduceBattles(c.Cache)
|
battles := ProduceBattles(c.Cache)
|
||||||
|
|
||||||
// 03. Товары загружаются на корабли, находящиеся в начале грузовых маршрутов, и корабли входят в гиперпространство.
|
// 03. Товары загружаются на корабли, находящиеся в начале грузовых маршрутов, и корабли входят в гиперпространство (но ещё не полетели)
|
||||||
c.Cache.EnrouteGroups()
|
c.Cache.EnrouteGroups()
|
||||||
|
|
||||||
|
// 04. Корабли пролетают сквозь гиперпространство.
|
||||||
|
c.Cache.MoveShipGroups()
|
||||||
|
|
||||||
|
// 05. Корабли, где это возможно, объединяются в группы.
|
||||||
|
c.Cache.CmdJoinEqualGroups()
|
||||||
|
|
||||||
|
// 06. Враждующие корабли снова вступают в схватку (это происходит после выхода из гиперпространства).
|
||||||
|
battles = append(battles, ProduceBattles(c.Cache)...)
|
||||||
|
|
||||||
/*** Last steps ***/
|
/*** Last steps ***/
|
||||||
|
|
||||||
// Store battles
|
// Store battles
|
||||||
|
|||||||
@@ -137,9 +137,6 @@ func TestEnrouteGroups_SplitGroup(t *testing.T) {
|
|||||||
c, g := newCache()
|
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, "COL", 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, "CAP", R0_Planet_0_num, R0_Planet_2_num))
|
|
||||||
// assert.NoError(t, g.SetRoute(Race_0.Name, "EMP", R0_Planet_2_num, R1_Planet_1_num))
|
|
||||||
|
|
||||||
c.MustPlanet(R0_Planet_0_num).Colonists = 65
|
c.MustPlanet(R0_Planet_0_num).Colonists = 65
|
||||||
|
|
||||||
@@ -162,9 +159,6 @@ func TestEnrouteGroups_GroupSorting(t *testing.T) {
|
|||||||
c, g := newCache()
|
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, "COL", 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, "CAP", R0_Planet_0_num, R0_Planet_2_num))
|
|
||||||
// assert.NoError(t, g.SetRoute(Race_0.Name, "EMP", R0_Planet_2_num, R1_Planet_1_num))
|
|
||||||
|
|
||||||
c.MustPlanet(R0_Planet_0_num).Colonists = 100
|
c.MustPlanet(R0_Planet_0_num).Colonists = 100
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package controller
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"iter"
|
||||||
|
|
||||||
|
"github.com/iliadenisov/galaxy/internal/model/game"
|
||||||
|
"github.com/iliadenisov/galaxy/internal/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Cache) MoveShipGroups() {
|
||||||
|
moved := make(map[int]bool)
|
||||||
|
for i := range c.listMoveableGroupIds() {
|
||||||
|
if v, ok := moved[i]; ok && v {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
sg := c.ShipGroup(i)
|
||||||
|
|
||||||
|
if sg.FleetID != nil {
|
||||||
|
fi := c.MustFleetIndex(*sg.FleetID)
|
||||||
|
delta := c.FleetSpeed(c.g.Fleets[fi])
|
||||||
|
for fgi := range c.fleetGroupIds(c.RaceIndex(sg.OwnerID), c.MustFleetIndex(*sg.FleetID)) {
|
||||||
|
c.moveShipGroup(fgi, delta)
|
||||||
|
moved[fgi] = true
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
c.moveShipGroup(i, sg.Speed(c.ShipGroupShipClass(i)))
|
||||||
|
moved[i] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) moveShipGroup(i int, delta float64) {
|
||||||
|
sg := c.ShipGroup(i)
|
||||||
|
originX, originY, ok := sg.Coord()
|
||||||
|
if !ok {
|
||||||
|
panic(fmt.Sprintf("ship group state invalid: %v", sg.State()))
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
if arrived {
|
||||||
|
sg.StateInSpace = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) listMoveableGroupIds() iter.Seq[int] {
|
||||||
|
return func(yield func(int) bool) {
|
||||||
|
for i := range c.ShipGroupsIndex() {
|
||||||
|
sg := c.ShipGroup(i)
|
||||||
|
state := sg.State()
|
||||||
|
if !(state == game.StateInOrbit || state == game.StateLaunched || state == game.StateInSpace) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !yield(i) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package controller_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"slices"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/iliadenisov/galaxy/internal/model/game"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestListMoveableGroupIds(t *testing.T) {
|
||||||
|
c, g := newCache()
|
||||||
|
|
||||||
|
// 1: idx = 0 / [v] Non-Fleet group
|
||||||
|
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 10))
|
||||||
|
|
||||||
|
// 2: idx = 1 / [v] In-Fleet group
|
||||||
|
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Gunship, R0_Planet_0_num, 1))
|
||||||
|
// 3: idx = 2 / [v] In-Fleet group
|
||||||
|
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", 2, 0))
|
||||||
|
assert.NoError(t, g.JoinShipGroupToFleet(Race_0.Name, "Fleet", 3, 0))
|
||||||
|
|
||||||
|
// 4: idx = 3 / [v] In_Space
|
||||||
|
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 7))
|
||||||
|
c.ShipGroup(3).StateInSpace = &game.InSpace{
|
||||||
|
Origin: 2,
|
||||||
|
Range: 31.337,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5: idx = 4 / [x] In_Upgrage
|
||||||
|
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 7))
|
||||||
|
c.ShipGroup(4).StateUpgrade = &game.InUpgrade{
|
||||||
|
UpgradeTech: []game.UpgradePreference{},
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6: idx = 5 / [v] Just launched group
|
||||||
|
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 10))
|
||||||
|
assert.NoError(t, g.SendGroup(Race_0.Name, 6, R0_Planet_2_num, 0))
|
||||||
|
|
||||||
|
movableGroups := slices.Collect(c.ListMoveableGroupIds())
|
||||||
|
assert.Len(t, movableGroups, 5)
|
||||||
|
for _, i := range movableGroups {
|
||||||
|
sg := c.ShipGroup(i)
|
||||||
|
assert.NotEqual(t, game.StateUpgrade, sg.State())
|
||||||
|
assert.NotEqual(t, game.StateTransfer, sg.State()) // TODO: Transfer state movable or not?
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -72,10 +72,16 @@ func (c *Cache) LaunchShips(sg *game.ShipGroup, destination uint) *game.ShipGrou
|
|||||||
for i := range c.ShipGroupsIndex() {
|
for i := range c.ShipGroupsIndex() {
|
||||||
if c.ShipGroup(i).OwnerID == sg.OwnerID && c.ShipGroup(i).Index == sg.Index {
|
if c.ShipGroup(i).OwnerID == sg.OwnerID && c.ShipGroup(i).Index == sg.Index {
|
||||||
state := c.ShipGroup(i).State()
|
state := c.ShipGroup(i).State()
|
||||||
if state != game.StateInOrbit && state != game.StateLaunched {
|
var p *game.Planet
|
||||||
|
switch state {
|
||||||
|
case game.StateInOrbit:
|
||||||
|
p = c.MustPlanet(sg.Destination)
|
||||||
|
case game.StateLaunched:
|
||||||
|
p = c.MustPlanet(sg.StateInSpace.Origin)
|
||||||
|
default:
|
||||||
panic("state invalid")
|
panic("state invalid")
|
||||||
}
|
}
|
||||||
c.g.ShipGroups[i] = LaunchShips(*sg, destination)
|
c.g.ShipGroups[i] = LaunchShips(*sg, destination, p.X, p.Y)
|
||||||
return &c.g.ShipGroups[i]
|
return &c.g.ShipGroups[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -96,9 +102,11 @@ func (c *Cache) UnsendShips(sg *game.ShipGroup) *game.ShipGroup {
|
|||||||
panic("ship group not found")
|
panic("ship group not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
func LaunchShips(sg game.ShipGroup, destination uint) game.ShipGroup {
|
func LaunchShips(sg game.ShipGroup, destination uint, originX, originY float64) game.ShipGroup {
|
||||||
sg.StateInSpace = &game.InSpace{
|
sg.StateInSpace = &game.InSpace{
|
||||||
Origin: sg.Destination,
|
Origin: sg.Destination,
|
||||||
|
X: originX,
|
||||||
|
Y: originY,
|
||||||
}
|
}
|
||||||
sg.Destination = destination
|
sg.Destination = destination
|
||||||
return sg
|
return sg
|
||||||
|
|||||||
@@ -44,26 +44,32 @@ func TestSendGroup(t *testing.T) {
|
|||||||
g.SendGroup(Race_0.Name, 1, 3, 0),
|
g.SendGroup(Race_0.Name, 1, 3, 0),
|
||||||
e.GenericErrorText(e.ErrSendUnreachableDestination))
|
e.GenericErrorText(e.ErrSendUnreachableDestination))
|
||||||
|
|
||||||
assert.NoError(t, g.SendGroup(Race_0.Name, 1, 2, 3)) // send 3 of 10
|
assert.NoError(t, g.SendGroup(Race_0.Name, 1, R0_Planet_2_num, 3)) // send 3 of 10
|
||||||
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 4)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 4)
|
||||||
assert.Equal(t, uint(7), c.ShipGroup(0).Number)
|
assert.Equal(t, uint(7), c.ShipGroup(0).Number)
|
||||||
assert.Equal(t, game.StateInOrbit, c.ShipGroup(0).State())
|
assert.Equal(t, game.StateInOrbit, c.ShipGroup(0).State())
|
||||||
assert.Equal(t, uint(3), c.ShipGroup(3).Number)
|
assert.Equal(t, uint(3), c.ShipGroup(3).Number)
|
||||||
assert.Equal(t, game.StateLaunched, c.ShipGroup(3).State())
|
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.NoError(t, g.SendGroup(Race_0.Name, 4, 0, 2)) // un-send 2 of 3
|
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)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 4)
|
||||||
assert.Equal(t, uint(9), c.MustShipGroup(Race_0_idx, 1).Number)
|
assert.Equal(t, uint(9), c.MustShipGroup(Race_0_idx, 1).Number)
|
||||||
assert.Equal(t, game.StateInOrbit, c.MustShipGroup(Race_0_idx, 1).State())
|
assert.Equal(t, game.StateInOrbit, c.MustShipGroup(Race_0_idx, 1).State())
|
||||||
assert.Equal(t, uint(1), c.MustShipGroup(Race_0_idx, 4).Number)
|
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.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.NoError(t, g.SendGroup(Race_0.Name, 4, 0, 0)) // un-send the rest 1
|
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)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 3)
|
||||||
assert.Equal(t, uint(10), c.MustShipGroup(Race_0_idx, 1).Number)
|
assert.Equal(t, uint(10), c.MustShipGroup(Race_0_idx, 1).Number)
|
||||||
assert.Equal(t, game.StateInOrbit, c.MustShipGroup(Race_0_idx, 1).State())
|
assert.Equal(t, game.StateInOrbit, c.MustShipGroup(Race_0_idx, 1).State())
|
||||||
|
|
||||||
assert.NoError(t, g.SendGroup(Race_0.Name, 1, 2, 0))
|
assert.NoError(t, g.SendGroup(Race_0.Name, 1, R0_Planet_2_num, 0))
|
||||||
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 3)
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 3)
|
||||||
assert.Equal(t, uint(10), c.MustShipGroup(Race_0_idx, 1).Number)
|
assert.Equal(t, uint(10), c.MustShipGroup(Race_0_idx, 1).Number)
|
||||||
assert.Equal(t, game.StateLaunched, c.MustShipGroup(Race_0_idx, 1).State())
|
assert.Equal(t, game.StateLaunched, c.MustShipGroup(Race_0_idx, 1).State())
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ type InSpace struct {
|
|||||||
X float64 `json:"x"`
|
X float64 `json:"x"`
|
||||||
Y float64 `json:"y"`
|
Y float64 `json:"y"`
|
||||||
// zero is for Launched status
|
// zero is for Launched status
|
||||||
// TODO: calculate range dynamically
|
// TODO: calculate range dynamically -BUT- if affects ShipGroup.State()
|
||||||
Range float64 `json:"range"`
|
Range float64 `json:"range"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,6 +154,14 @@ func (sg ShipGroup) OnPlanet() (uint, bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sg ShipGroup) Coord() (float64, float64, bool) {
|
||||||
|
state := sg.State()
|
||||||
|
if state == StateInSpace || state == StateLaunched {
|
||||||
|
return sg.StateInSpace.X, sg.StateInSpace.Y, true
|
||||||
|
}
|
||||||
|
return 0, 0, false
|
||||||
|
}
|
||||||
|
|
||||||
func (sg ShipGroup) Equal(other ShipGroup) bool {
|
func (sg ShipGroup) Equal(other ShipGroup) bool {
|
||||||
return sg.OwnerID == other.OwnerID &&
|
return sg.OwnerID == other.OwnerID &&
|
||||||
sg.TypeID == other.TypeID &&
|
sg.TypeID == other.TypeID &&
|
||||||
|
|||||||
+67
-2
@@ -3,6 +3,72 @@ package util
|
|||||||
import "math"
|
import "math"
|
||||||
|
|
||||||
func ShortDistance(w, h uint32, x1, y1, x2, y2 float64) float64 {
|
func ShortDistance(w, h uint32, x1, y1, x2, y2 float64) float64 {
|
||||||
|
return math.Hypot(deltas(w, h, x1, y1, x2, y2))
|
||||||
|
}
|
||||||
|
|
||||||
|
func NextTravelCoord(w, h uint32, x1, y1, x2, y2, delta float64) (float64, float64, bool) {
|
||||||
|
deltaX, deltaY := deltas(w, h, x1, y1, x2, y2)
|
||||||
|
distance := math.Hypot(deltaX, deltaY)
|
||||||
|
if distance <= delta {
|
||||||
|
return x2, y2, true
|
||||||
|
}
|
||||||
|
// TODO: refactor - remove extra vars
|
||||||
|
xa := 0.
|
||||||
|
ya := 0.
|
||||||
|
xb := deltaX
|
||||||
|
yb := deltaY
|
||||||
|
d := distance
|
||||||
|
d2 := delta
|
||||||
|
xc := xa - (d2*(xa-xb))/d
|
||||||
|
yc := ya - (d2*(ya-yb))/d
|
||||||
|
|
||||||
|
// ---
|
||||||
|
var tx, ty float64
|
||||||
|
|
||||||
|
if math.Abs(x2-x1) > float64(w/2) {
|
||||||
|
// moving across X boundary
|
||||||
|
if x2 < x1 {
|
||||||
|
// moving across higher border
|
||||||
|
tx = math.Mod(x1+xc, float64(w))
|
||||||
|
} else {
|
||||||
|
// moving across lower border
|
||||||
|
tx = x1 - xc
|
||||||
|
if tx < 0 {
|
||||||
|
tx = float64(w) + tx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if x2 < x1 {
|
||||||
|
tx = x1 - xc
|
||||||
|
} else {
|
||||||
|
tx = x1 + xc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if math.Abs(y2-y1) > float64(h/2) {
|
||||||
|
// moving across Y boundary
|
||||||
|
if y2 < y1 {
|
||||||
|
// moving across higher border
|
||||||
|
ty = math.Mod(y1+yc, float64(h))
|
||||||
|
} else {
|
||||||
|
// moving across lower border
|
||||||
|
ty = y1 - yc
|
||||||
|
if ty < 0 {
|
||||||
|
ty = float64(h) + ty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if y2 < y1 {
|
||||||
|
ty = y1 - yc
|
||||||
|
} else {
|
||||||
|
ty = y1 + yc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tx, ty, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func deltas(w, h uint32, x1, y1, x2, y2 float64) (float64, float64) {
|
||||||
dx := math.Abs(x2 - x1)
|
dx := math.Abs(x2 - x1)
|
||||||
dy := math.Abs(y2 - y1)
|
dy := math.Abs(y2 - y1)
|
||||||
if dx > float64(w/2) {
|
if dx > float64(w/2) {
|
||||||
@@ -11,6 +77,5 @@ func ShortDistance(w, h uint32, x1, y1, x2, y2 float64) float64 {
|
|||||||
if dy > float64(h/2) {
|
if dy > float64(h/2) {
|
||||||
dy = float64(h) - dy
|
dy = float64(h) - dy
|
||||||
}
|
}
|
||||||
return math.Sqrt(math.Pow(dx, 2) + math.Pow(dy, 2))
|
return dx, dy
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,3 +25,32 @@ func TestShortDistance(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNextTravelCoord(t *testing.T) {
|
||||||
|
for i, tc := range []struct {
|
||||||
|
w, h uint32
|
||||||
|
ox, oy, dx, dy, delta float64
|
||||||
|
tx, ty float64
|
||||||
|
arrived bool
|
||||||
|
}{
|
||||||
|
{w: 10, h: 10, ox: 0.0, oy: 0.0, dx: 2.0, dy: 0.0, delta: 1.0, tx: 1.0, ty: 0.0, arrived: false},
|
||||||
|
{w: 10, h: 10, ox: 0.0, oy: 0.0, dx: 0.0, dy: 2.0, delta: 1.0, tx: 0.0, ty: 1.0, arrived: false},
|
||||||
|
|
||||||
|
{w: 10, h: 10, ox: 1.0, oy: 1.0, dx: 9.0, dy: 1.0, delta: 1.0, tx: 0.0, ty: 1.0, arrived: false},
|
||||||
|
{w: 10, h: 10, ox: 1.0, oy: 9.5, dx: 1.0, dy: 1.0, delta: 1.0, tx: 1.0, ty: 0.5, arrived: false},
|
||||||
|
|
||||||
|
{w: 10, h: 10, ox: 1.0, oy: 1.0, dx: 5.0, dy: 5.0, delta: 2.0, tx: 2.414, ty: 2.414, arrived: false},
|
||||||
|
{w: 10, h: 10, ox: 1.0, oy: 1.0, dx: 9.0, dy: 9.0, delta: 2.0, tx: 9.586, ty: 9.586, arrived: false},
|
||||||
|
|
||||||
|
{w: 10, h: 10, ox: 5.0, oy: 5.0, dx: 9.0, dy: 9.0, delta: 6.0, tx: 9.0, ty: 9.0, arrived: true},
|
||||||
|
{w: 10, h: 10, ox: 6.0, oy: 6.0, dx: 10.0, dy: 10.0, delta: 6.0, tx: 10.0, ty: 10.0, arrived: true},
|
||||||
|
{w: 10, h: 10, ox: 1.0, oy: 2.0, dx: 7.0, dy: 8.0, delta: 6.0, tx: 7.0, ty: 8.0, arrived: true},
|
||||||
|
} {
|
||||||
|
t.Run(fmt.Sprint(i), func(t *testing.T) {
|
||||||
|
tx, ty, arrived := util.NextTravelCoord(tc.w, tc.h, tc.ox, tc.oy, tc.dx, tc.dy, tc.delta)
|
||||||
|
assert.Equal(t, tc.arrived, arrived)
|
||||||
|
assert.Equal(t, tc.tx, number.Fixed3(tx))
|
||||||
|
assert.Equal(t, tc.ty, number.Fixed3(ty))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user