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));
}