Repository: robojackets/robocup-software
Issue: When stopped, halting and re-stopping play causes robots to shift positions

This seems kind of weird, and might be a red flag of some bigger problems in our role assignments.

When we stop play, halt, and restart play, no robots should move, because everything is in their correct places. However, robots often switch places for no apparent reason. This may be due to the ball obstacle for some reason, but this needs to be triaged to figure out the underlying cause.

Changed files:
soccer/gameplay/tactics/stopped/circle_near_ball.py, soccer/gameplay/tactics/stopped/circle_on_center.py

diff --git a/soccer/gameplay/tactics/stopped/circle_near_ball.py b/soccer/gameplay/tactics/stopped/circle_near_ball.py
index 09d36175b4..4bcc95bb05 100644
--- a/soccer/gameplay/tactics/stopped/circle_near_ball.py
+++ b/soccer/gameplay/tactics/stopped/circle_near_ball.py
@@ -16,6 +16,7 @@ class CircleNearBall(composite_behavior.CompositeBehavior):
     def __init__(self):
         super().__init__(continuous=True)
 
+        self.num_robots = 0
         self.add_transition(behavior.Behavior.State.start,
                             behavior.Behavior.State.running, lambda: True,
                             'immediately')
@@ -28,14 +29,12 @@ class CircleNearBall(composite_behavior.CompositeBehavior):
                             lambda: not self.all_subbehaviors_completed(),
                             "robots aren't lined up")
 
-        i = 0
-        for pt in self.get_circle_points(6):
-            self.add_subbehavior(
-                skills.move.Move(pt),
-                name="robot" + str(i),
-                required=False,
-                priority=6 - i)
-            i = i + 1
+        #create move behaviors with no position (we can't assign position because we don't know how many bots we have)
+        for i in range(6):
+            self.add_subbehavior(skills.move.Move(),
+                                 name="robot" + str(i),
+                                 required=False,
+                                 priority=6 - i)
 
     def get_circle_points(self, num_of_points):
         radius = constants.Field.CenterRadius + constants.Robot.Radius + 0.01
@@ -124,23 +123,6 @@ class CircleNearBall(composite_behavior.CompositeBehavior):
 
         return final_points
 
-    def execute_completed(self):
-        num_robots = 0
-        for b in self.all_subbehaviors():
-            if b.robot is not None:
-                num_robots += 1
-
-        i = 0
-        for pt in self.get_circle_points(num_robots):
-            self.subbehavior_with_name("robot" + str(i)).pos = pt
-            i = i + 1
-
-        # set robot attributes
-        for b in self.all_subbehaviors():
-            if b.robot is not None:
-                b.robot.set_avoid_ball_radius(constants.Field.CenterRadius)
-                b.robot.face(main.ball().pos)
-
     # Makes an angle > 0, < pi * 2
     def normalize_angle(self, angle):
         # TODO make this O(1) and move to cpp
@@ -155,11 +137,22 @@ class CircleNearBall(composite_behavior.CompositeBehavior):
         for b in self.all_subbehaviors():
             if b.robot is not None:
                 num_robots += 1
-
-        i = 0
-        for pt in self.get_circle_points(num_robots):
+        #if the number of robots has changed, recreate move behaviors to match new number of robots
+        if (self.num_robots != num_robots):
+            self.num_robots = num_robots
+            self.remove_all_subbehaviors()
+            for i in range(6):
+                self.add_subbehavior(skills.move.Move(),
+                                     name="robot" + str(i),
+                                     required=False,
+                                     priority=6 - i)
+        #assign destinations for the number of robots we have
+        for i, pt in enumerate(self.get_circle_points(num_robots)):
             self.subbehavior_with_name("robot" + str(i)).pos = pt
-            i = i + 1
+
+        #unassign destinations from behaviors without robots
+        for i in range(num_robots, 6):
+            self.subbehavior_with_name("robot" + str(i)).pos = None
 
         # set robot attributes
         for b in self.all_subbehaviors():
diff --git a/soccer/gameplay/tactics/stopped/circle_on_center.py b/soccer/gameplay/tactics/stopped/circle_on_center.py
index 2b2f7e501b..cde5893114 100644
--- a/soccer/gameplay/tactics/stopped/circle_on_center.py
+++ b/soccer/gameplay/tactics/stopped/circle_on_center.py
@@ -26,26 +26,15 @@ class CircleOnCenter(composite_behavior.CompositeBehavior):
                             "robots aren't lined up")
 
         self.min_robots = min_robots
+        self.num_robots = 0
 
-        # Define circle to circle up on
-        radius = constants.Field.CenterRadius + constants.Robot.Radius + 0.01
-
-        perRobot = (2 * constants.Robot.Radius * 1.25) / radius
-
-        ball_pos = robocup.Point(0, constants.Field.Length / 2)
-
-        dirvec = (robocup.Point(0, 0) - ball_pos).normalized() * radius
-
+        #create move behaviors with no position (we can't assign position because we don't know how many bots we have)
         for i in range(6):
             req = i < min_robots
-
-            pt = ball_pos + dirvec
-            self.add_subbehavior(
-                skills.move.Move(pt),
-                name="robot" + str(i),
-                required=req,
-                priority=6 - i)
-            dirvec.rotate(robocup.Point(0, 0), perRobot)
+            self.add_subbehavior(skills.move.Move(),
+                                 name="robot" + str(i),
+                                 required=req,
+                                 priority=6 - i)
 
     def goto_center(self):
         num_robots = 0
@@ -53,6 +42,16 @@ class CircleOnCenter(composite_behavior.CompositeBehavior):
             if b.robot is not None:
                 num_robots += 1
 
+        #if the number of robots has changed, recreate move behaviors to match new number of robots
+        if (self.num_robots != num_robots):
+            self.num_robots = num_robots
+            self.remove_all_subbehaviors()
+            for pt in range(6):
+                self.add_subbehavior(skills.move.Move(),
+                                     name="robot" + str(pt),
+                                     required=False,
+                                     priority=6 - pt)
+
         num_robots = max(self.min_robots, num_robots)
 
         radius = constants.Field.CenterRadius + constants.Robot.Radius + 0.01
@@ -64,7 +63,8 @@ class CircleOnCenter(composite_behavior.CompositeBehavior):
         dirvec = (robocup.Point(0, 0) - ball_pos).normalized() * radius
         dirvec.rotate(robocup.Point(0, 0), -perRobot * ((num_robots - 1) / 2))
 
-        for i in range(6):
+        #assign points to the behaviors with robots
+        for i in range(num_robots):
             pt = ball_pos + dirvec
             self.subbehavior_with_name("robot" + str(i)).pos = pt
             dirvec.rotate(robocup.Point(0, 0), perRobot)
@@ -75,8 +75,5 @@ class CircleOnCenter(composite_behavior.CompositeBehavior):
                 b.robot.set_avoid_ball_radius(constants.Field.CenterRadius)
                 b.robot.face(main.ball().pos)
 
-    def execute_completed(self):
-        self.goto_center()
-
     def execute_running(self):
         self.goto_center()