From 2778b61c66c8c1f8a6aa1dfeefa928b2405cb354 Mon Sep 17 00:00:00 2001 From: Nicolai Ommer Date: Sat, 15 Sep 2018 21:55:54 +0200 Subject: [PATCH] [test] Add tests for ball placement and fixed some issues --- internal/app/controller/placementPos.go | 33 +++-- internal/app/controller/placementPos_test.go | 135 +++++++++++++++++++ internal/app/controller/state.go | 9 ++ 3 files changed, 169 insertions(+), 8 deletions(-) create mode 100644 internal/app/controller/placementPos_test.go diff --git a/internal/app/controller/placementPos.go b/internal/app/controller/placementPos.go index 7bbad247..b76ec850 100644 --- a/internal/app/controller/placementPos.go +++ b/internal/app/controller/placementPos.go @@ -18,17 +18,32 @@ func (e *Engine) BallPlacementPos() *Location { switch event.Type { case GameEventBallLeftFieldTouchLine: - return e.validateProtoLocation(event.Details.BallLeftFieldTouchLine.Location) + if event.Details.BallLeftFieldTouchLine.Location != nil { + location := mapProtoLocation(event.Details.BallLeftFieldTouchLine.Location) + x := e.Geometry.FieldLength/2 - e.Geometry.PlacementOffsetGoalLine + if math.Abs(location.X) > x { + location.X = math.Copysign(x, location.X) + } + y := e.Geometry.FieldWidth/2 - e.Geometry.PlacementOffsetTouchLine + location.Y = math.Copysign(y, location.Y) + return e.validateLocation(location) + } + return nil case GameEventBallLeftFieldGoalLine: - if event.Details.BallLeftFieldGoalLine.Location != nil && e.isGoalKick(event) { + if event.Details.BallLeftFieldGoalLine.Location != nil { location := mapProtoLocation(event.Details.BallLeftFieldGoalLine.Location) - maxX := e.Geometry.FieldLength/2 - e.Geometry.PlacementOffsetGoalLineGoalKick - if math.Abs(location.X) > maxX { - location.X = math.Copysign(maxX, location.X) + var x float64 + if e.isGoalKick(event) { + x = e.Geometry.FieldLength/2 - e.Geometry.PlacementOffsetGoalLineGoalKick + } else { + x = e.Geometry.FieldLength/2 - e.Geometry.PlacementOffsetGoalLine } + location.X = math.Copysign(x, location.X) + y := e.Geometry.FieldWidth/2 - e.Geometry.PlacementOffsetTouchLine + location.Y = math.Copysign(y, location.Y) return e.validateLocation(location) } - return e.validateProtoLocation(event.Details.BallLeftFieldGoalLine.Location) + return nil case GameEventIcing: return e.validateProtoLocation(event.Details.Icing.KickLocation) case GameEventGoal: @@ -129,9 +144,11 @@ func (e *Engine) validateLocation(location *Location) *Location { func (e *Engine) movePositionOutOfDefenseArea(location *Location) { maxX := e.Geometry.FieldLength/2 - e.Geometry.DefenseAreaDepth - e.Geometry.PlacementOffsetDefenseArea - minY := e.Geometry.DefenseAreaWidth + e.Geometry.PlacementOffsetDefenseArea + minY := e.Geometry.DefenseAreaWidth/2 + e.Geometry.PlacementOffsetDefenseArea if math.Abs(location.X) > maxX && math.Abs(location.Y) < minY { - if math.Abs(maxX-math.Abs(location.X)) < math.Abs(minY-math.Abs(location.Y)) { + diffX := math.Abs(maxX - math.Abs(location.X)) + diffY := math.Abs(minY - math.Abs(location.Y)) + if diffX < diffY { location.X = math.Copysign(maxX, location.X) } else { location.Y = math.Copysign(minY, location.Y) diff --git a/internal/app/controller/placementPos_test.go b/internal/app/controller/placementPos_test.go new file mode 100644 index 00000000..4de70142 --- /dev/null +++ b/internal/app/controller/placementPos_test.go @@ -0,0 +1,135 @@ +package controller + +import ( + "github.com/RoboCup-SSL/ssl-game-controller/pkg/refproto" + "math" + "testing" +) + +func TestEngine_BallPlacementPos(t *testing.T) { + config := DefaultConfig() + engine := NewEngine(config.Game) + engine.State.TeamState[TeamYellow].OnPositiveHalf = true + engine.State.TeamState[TeamBlue].OnPositiveHalf = false + engine.Geometry.PlacementOffsetTouchLine = 0.2 + engine.Geometry.PlacementOffsetGoalLine = 0.3 // avoid confusion with touchLine value + engine.Geometry.PlacementOffsetGoalLineGoalKick = 1.0 + fw := engine.Geometry.FieldWidth + fl := engine.Geometry.FieldLength + dd := engine.Geometry.DefenseAreaDepth + dw := engine.Geometry.DefenseAreaWidth + ot := engine.Geometry.PlacementOffsetTouchLine + og := engine.Geometry.PlacementOffsetGoalLine + ogg := engine.Geometry.PlacementOffsetGoalLineGoalKick + od := engine.Geometry.PlacementOffsetDefenseArea + + // ball left field touch line + setBallLeftFieldTouchLine(&engine, Location{0, fw / 2}.toProto()) + assertSimilar(t, &engine, Location{0, fw/2 - ot}) + + setBallLeftFieldTouchLine(&engine, Location{0.1, fw / 2}.toProto()) + assertSimilar(t, &engine, Location{0.1, fw/2 - ot}) + + setBallLeftFieldTouchLine(&engine, Location{0, 42}.toProto()) + assertSimilar(t, &engine, Location{0, fw/2 - ot}) + + setBallLeftFieldTouchLine(&engine, Location{0, 1}.toProto()) + assertSimilar(t, &engine, Location{0, fw/2 - ot}) + + setBallLeftFieldTouchLine(&engine, Location{0, -fw / 2}.toProto()) + assertSimilar(t, &engine, Location{0, -(fw/2 - ot)}) + + setBallLeftFieldTouchLine(&engine, Location{fl / 2, fw / 2}.toProto()) + assertSimilar(t, &engine, Location{fl/2 - og, fw/2 - ot}) + + setBallLeftFieldTouchLine(&engine, Location{-fl / 2, fw / 2}.toProto()) + assertSimilar(t, &engine, Location{-(fl/2 - og), fw/2 - ot}) + + // ball left field goal line + setBallLeftFieldGoalLine(&engine, Location{fl / 2, fw / 2}.toProto(), TeamYellow) + assertSimilar(t, &engine, Location{fl/2 - ogg, fw/2 - ot}) + + setBallLeftFieldGoalLine(&engine, Location{-fl / 2, fw / 2}.toProto(), TeamYellow) + assertSimilar(t, &engine, Location{-(fl/2 - og), fw/2 - ot}) + + setBallLeftFieldGoalLine(&engine, Location{fl / 2, fw / 4}.toProto(), TeamYellow) + assertSimilar(t, &engine, Location{fl/2 - ogg, fw/2 - ot}) + + setBallLeftFieldGoalLine(&engine, Location{fl / 2, fw / 4}.toProto(), TeamBlue) + assertSimilar(t, &engine, Location{fl/2 - og, fw/2 - ot}) + + setBallLeftFieldGoalLine(&engine, Location{fl / 2, 0}.toProto(), TeamBlue) + assertSimilar(t, &engine, Location{fl/2 - og, fw/2 - ot}) + + setBallLeftFieldGoalLine(&engine, Location{fl / 2, -fw}.toProto(), TeamBlue) + assertSimilar(t, &engine, Location{fl/2 - og, -(fw/2 - ot)}) + + // bot crash unique + setBotCrashUnique(&engine, Location{0, 0}.toProto()) + assertSimilar(t, &engine, Location{0, 0}) + + setBotCrashUnique(&engine, Location{1, -2}.toProto()) + assertSimilar(t, &engine, Location{1, -2}) + + setBotCrashUnique(&engine, Location{1, -42}.toProto()) + assertSimilar(t, &engine, Location{1, -(fw/2 - ot)}) + + setBotCrashUnique(&engine, Location{1, 42}.toProto()) + assertSimilar(t, &engine, Location{1, fw/2 - ot}) + + setBotCrashUnique(&engine, Location{fl / 2, -42}.toProto()) + assertSimilar(t, &engine, Location{fl/2 - og, -(fw/2 - ot)}) + + setBotCrashUnique(&engine, Location{-fl / 2, -42}.toProto()) + assertSimilar(t, &engine, Location{-(fl/2 - og), -(fw/2 - ot)}) + + setBotCrashUnique(&engine, Location{42, 0}.toProto()) + assertSimilar(t, &engine, Location{fl/2 - dd - od, 0}) + + setBotCrashUnique(&engine, Location{fl/2 - 0.1, dw/2 - 0.1}.toProto()) + assertSimilar(t, &engine, Location{fl/2 - og, dw/2 + od}) + + setBotCrashUnique(&engine, Location{fl/2 - dd, dw/2 - 0.1}.toProto()) + assertSimilar(t, &engine, Location{fl/2 - dd - od, dw/2 - 0.1}) +} + +func assertSimilar(t *testing.T, engine *Engine, expected Location) { + placementPos := engine.BallPlacementPos() + if !similar(expected, *placementPos) { + t.Fatalf("Expected placement pos to be %v, but was %v", expected, *placementPos) + } +} + +func setBallLeftFieldTouchLine(engine *Engine, eventLocation *refproto.Location) { + var byTeam = TeamYellow.toProto() + engine.State.GameEvents = []*GameEvent{{ + Type: GameEventBallLeftFieldTouchLine, + Details: GameEventDetails{ + BallLeftFieldTouchLine: &refproto.GameEvent_BallLeftFieldEvent{ + ByTeam: &byTeam, + Location: eventLocation}}}} +} + +func setBallLeftFieldGoalLine(engine *Engine, eventLocation *refproto.Location, placingTeam Team) { + var byTeam = placingTeam.Opposite().toProto() + engine.State.GameEvents = []*GameEvent{{ + Type: GameEventBallLeftFieldGoalLine, + Details: GameEventDetails{ + BallLeftFieldGoalLine: &refproto.GameEvent_BallLeftFieldEvent{ + ByTeam: &byTeam, + Location: eventLocation}}}} +} + +func setBotCrashUnique(engine *Engine, eventLocation *refproto.Location) { + var byTeam = TeamYellow.toProto() + engine.State.GameEvents = []*GameEvent{{ + Type: GameEventBotCrashUnique, + Details: GameEventDetails{ + BotCrashUnique: &refproto.GameEvent_BotCrashUnique{ + ByTeam: &byTeam, + Location: eventLocation}}}} +} + +func similar(l1 Location, l2 Location) bool { + return math.Abs(l1.X-l2.X) < 1e-4 && math.Abs(l1.Y-l2.Y) < 1e-4 +} diff --git a/internal/app/controller/state.go b/internal/app/controller/state.go index 21b794a2..91e77dc1 100644 --- a/internal/app/controller/state.go +++ b/internal/app/controller/state.go @@ -357,3 +357,12 @@ type Location struct { X float64 Y float64 } + +func (l Location) toProto() (p *refproto.Location) { + p = new(refproto.Location) + p.X = new(float32) + p.Y = new(float32) + *p.X = float32(l.X) + *p.Y = float32(l.Y) + return +}