diff --git a/Soccer/Knowledge/Knowledge.Defense.cs b/Soccer/Knowledge/Knowledge.Defense.cs index 684f7e75..2c4d0971 100644 --- a/Soccer/Knowledge/Knowledge.Defense.cs +++ b/Soccer/Knowledge/Knowledge.Defense.cs @@ -15,9 +15,6 @@ public partial class Knowledge [ConfigEntry] public static float PenaltyAreaExtensionSize { get; set; } = 200.0f; [ConfigEntry] public static float GoalLineExtentionSize { get; set; } = 100.0f; - private Common.Data.Ssl.Gc.Command? _lastRefCommand; - private Common.Time.Timestamp _oppRestartTimestamp; - public bool GoalieDiveAllowed { get; private set; } public bool BallIsGoaling { get; private set; } public float BallOwnGoalReachTime { get; private set; } diff --git a/Soccer/Plays/OurFreekick.cs b/Soccer/Plays/OurFreekick.cs index 71d9c78f..91ea9ee8 100644 --- a/Soccer/Plays/OurFreekick.cs +++ b/Soccer/Plays/OurFreekick.cs @@ -18,7 +18,10 @@ public Formation Tick() var zones = Context.Knowledge.SortedZonesByOffense; var bestOffenseZone = zones.Count > 0 ? zones.Peek() : null; - Draw.DrawCircle(bestOffenseZone.BestPosOffence, 200, Color.Amber, Options.Outline()); + if (bestOffenseZone != null) + { + Draw.DrawCircle(bestOffenseZone.BestPosOffence, 200, Color.Amber, Options.Outline()); + } var chipperTarget = bestOffenseZone?.BestPosOffence ?? Context.Field.OppGoal(); var chipPower = 0; diff --git a/Soccer/Tactics/BallPlacement.cs b/Soccer/Tactics/BallPlacement.cs index 45143aa1..1038a5f8 100644 --- a/Soccer/Tactics/BallPlacement.cs +++ b/Soccer/Tactics/BallPlacement.cs @@ -87,6 +87,7 @@ public BallPlacement(Robot.Robot robot, int placerId) var finalBallPos = Context.Referee.DesignatedPosition(); var ballPlacer1 = GetPlacer(1); var ballPlacer2 = GetPlacer(2); + if (ballPlacer1 == null || ballPlacer2 == null) return false; var middle = (ballPlacer1.Position + ballPlacer2.Position) / 2.0f; return Vector2.Distance(middle, finalBallPos) < 100f; }, BPStateDelay); @@ -311,28 +312,28 @@ public void Exit() var ballPlacer1 = GetPlacer(1); var ballPlacer2 = GetPlacer(2); - var direction = Vector2.Normalize((ballPlacer1.Position + ballPlacer2.Position) / 2.0f - finalBallPos); - if (ballPlacer1 != null && ballPlacer2 != null) { - //var direction = Vector2.Normalize((ballPlacer1.Position + ballPlacer2.Position) / 2.0f - finalBallPos); + var direction = Vector2.Normalize((ballPlacer1.Position + ballPlacer2.Position) / 2.0f - finalBallPos); tactic._ballPlacer2FinalPos = finalBallPos + direction * BPKissInitDistance; tactic._ballPlacer1FinalPos = finalBallPos - direction * BPKissInitDistance; + + var kissTouch2 = finalBallPos + direction * 75f; + var kissTouch1 = finalBallPos - direction * 75f; + + return new GoToPoint + { + Target = tactic.PlacerId == 1 ? kissTouch1 : kissTouch2, + VelocityProfile = VelocityProfile.Sooski, + NavigationFlags = NavigationFlags.NoObstacles | NavigationFlags.NoBallObstacle + }; } - //var axis = Vector2.Normalize((ballPlacer1?.Position ?? tactic.Robot.Position) - finalBallPos); - var kissTouch2 = finalBallPos + direction * 75f; - var kissTouch1 = finalBallPos - direction * 75f; - return new GoToPoint - { - Target = tactic.PlacerId == 1 ? kissTouch1 : kissTouch2, - VelocityProfile = VelocityProfile.Sooski, - NavigationFlags = NavigationFlags.NoObstacles | NavigationFlags.NoBallObstacle - }; + return null; } } diff --git a/SourceGen/SourceGen.csproj b/SourceGen/SourceGen.csproj index 4c1d17b3..dbc39b29 100644 --- a/SourceGen/SourceGen.csproj +++ b/SourceGen/SourceGen.csproj @@ -16,11 +16,11 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/Tests/Soccer/Plays/OurKickoffTests.cs b/Tests/Soccer/Plays/OurKickoffTests.cs index ab909a98..85653699 100644 --- a/Tests/Soccer/Plays/OurKickoffTests.cs +++ b/Tests/Soccer/Plays/OurKickoffTests.cs @@ -49,7 +49,7 @@ public void OurKickoff_ReturnsCorrectFormation() // Assert Assert.Equal(4, formation.RequiredRoles.Count); - Assert.Equal(3, formation.DesiredRoles.Count); + Assert.Equal(4, formation.DesiredRoles.Count); Assert.Contains(formation.RequiredRoles, r => r is Goalie); Assert.Contains(formation.RequiredRoles, r => r is Defender { DefId: 1 }); @@ -58,15 +58,15 @@ public void OurKickoff_ReturnsCorrectFormation() var attacker = (CircleBall)formation.RequiredRoles.First(r => r is CircleBall); // side = -Context.SideSign. Left is -1, so side = 1. - // mid5Pos = (0 + 1 * 150, 3000 - 300) = (150, 2700) - Assert.Equal(new Vector2(150f, 2700f), attacker.TargetPosition); + // mid5Pos = (0 - 1 * 150, 3000 - 300) = (-150, 2700) + Assert.Equal(new Vector2(-150f, 2700f), attacker.TargetPosition); Assert.Equal(0f, attacker.ShootPower); var mid5 = (Waiter)formation.DesiredRoles[0]; - Assert.Equal(new Vector2(150f, 2700f), mid5.Target); + Assert.Equal(new Vector2(-150f, 2700f), mid5.Target); var mid1 = (Waiter)formation.DesiredRoles[1]; - Assert.Equal(new Vector2(150f, -2700f), mid1.Target); + Assert.Equal(new Vector2(-150f, -2700f), mid1.Target); var mid2 = (Waiter)formation.DesiredRoles[2]; // mid2Pos = ballPos.PointOnConnectingLine(OwnGoal, 1000f) @@ -114,6 +114,6 @@ public void OurKickoff_KicksAfterTwoSeconds() // Assert var attacker = (CircleBall)formation.RequiredRoles.First(r => r is CircleBall); - Assert.Equal(5000f, attacker.ShootPower); + Assert.Equal(3000f, attacker.ShootPower); } } diff --git a/Tests/Soccer/Plays/StatefulPlayTests.cs b/Tests/Soccer/Plays/StatefulPlayTests.cs index bb701db4..3b7b3fc8 100644 --- a/Tests/Soccer/Plays/StatefulPlayTests.cs +++ b/Tests/Soccer/Plays/StatefulPlayTests.cs @@ -34,7 +34,7 @@ public void NormalPlay_DefendingState_UsesDefensiveAttackerAndMarking() var opponent = CreateOpponent(1, new Vector2(-2500f, 0f)); var knowledge = SetupContext( gameState: GameState.Running, - ballPosition: Vector2.Zero, + ballPosition: new Vector2(-1000f, 0f), ownRobots: ownRobots, oppRobots: [opponent]); @@ -81,7 +81,7 @@ public void Stop_DefendingState_UsesMarking() var opponent = CreateOpponent(1, new Vector2(-2500f, 200f)); var knowledge = SetupContext( gameState: GameState.Stop, - ballPosition: Vector2.Zero, + ballPosition: new Vector2(-1000f, 0f), ownRobots: ownRobots, oppRobots: [opponent]); diff --git a/Vision/Tracking/RobotMerger.cs b/Vision/Tracking/RobotMerger.cs index b217fb73..5b9b4f79 100644 --- a/Vision/Tracking/RobotMerger.cs +++ b/Vision/Tracking/RobotMerger.cs @@ -1,6 +1,7 @@ using System.Numerics; using Tyr.Common.Config; using Tyr.Common.Data.Ssl; +using Tyr.Common.Data; using Tyr.Common.Math; using Tyr.Common.Vision.Data; @@ -13,17 +14,35 @@ public partial class RobotMerger "Factor to weight stdDeviation during tracker merging, reasonable range: 1.0 - 2.0. High values lead to more jitter")] private static float MergePower { get; set; } = 1.5f; + // Bolt: eliminates ~15 allocs/frame (LINQ SelectMany/GroupBy/ToDictionary + enumerators) — using class-level dictionary and manual loops + // To verify: dotnet-counters monitor --counters System.Runtime[gen-0-gc-count,alloc-rate] -p + private readonly Dictionary> _trackersById = new(CommonConfigs.MaxRobots * 2); + public List Process(IEnumerable cameras, Timestamp timestamp) { - var trackersById = cameras - .SelectMany(camera => camera.Robots.Values) - .GroupBy(robot => robot.Id) - .ToDictionary(grouping => grouping.Key, grouping => grouping.ToList()); + foreach (var list in _trackersById.Values) + { + list.Clear(); + } + + foreach (var camera in cameras) + { + foreach (var robot in camera.Robots.Values) + { + if (!_trackersById.TryGetValue(robot.Id, out var trackers)) + { + trackers = new List(4); + _trackersById[robot.Id] = trackers; + } + trackers.Add(robot); + } + } - var mergedRobots = new List(); + var mergedRobots = new List(_trackersById.Count); - foreach (var (id, trackers) in trackersById) + foreach (var (id, trackers) in _trackersById) { + if (trackers.Count == 0) continue; mergedRobots.Add(Merge(id, trackers, timestamp)); }