Skip to content

Fix/defence algo - #119

Merged
energy-in-joles merged 154 commits into
mainfrom
fix/defence-algo
Jun 4, 2026
Merged

energy-in-joles merged 154 commits into
mainfrom
fix/defence-algo

Conversation

@fred-huang122

@fred-huang122 fred-huang122 commented May 10, 2026

Copy link
Copy Markdown
Collaborator

Adds updated defence positioning and goalkeeping logic, field-dimension-aware setup, and Fast Path Planning support. Also refreshes related strategy runner, simulator, and test coverage for custom field bounds and RSim/GRSim setup.

isaac0804 and others added 30 commits November 26, 2025 15:50
…st bugs

- Convert RefereeData from NamedTuple to @DataClass(eq=False) so the
  custom __eq__ is respected (NamedTuple.__eq__ cannot be overridden —
  tuple equality always wins)
- Use TYPE_CHECKING guard for TeamInfo import to avoid circular import
  (game/__init__ → Game → GameFrame → RefereeData → TeamInfo → game/__init__)
- __eq__ compares TeamInfo by .score and .goalkeeper (the mutable game-state
  fields) since TeamInfo has no structural __eq__ of its own
- Add __hash__ consistent with the subset of fields used in __eq__
- RefereeRefiner.add_new_referee_data: replace tuple slicing [1:] with ==
  (now correctly uses the custom __eq__)
- test_referee_unit.py: fix GameHistory() → GameHistory(10) (max_history
  is a required positional argument)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adopted main's SideRuntime refactor (my/opp sides) in strategy_runner.py
while preserving referee integration. Fixed imports to new data_processing
module paths. Resolved standard_ssl.py conflicts keeping RefereeData usage
from our branch with main's improved assertion.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add rsim integration tests for ball placement, direct free kick (ours
  and theirs), and kickoff positioning in test_referee_rsim.py
- Add 15 unit tests covering PrepareKickoffTheirsStep, DirectFreeOursStep,
  and DirectFreeTheirsStep action nodes in test_referee_unit.py
- Switch demo script control_scheme from dwa to pid

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace Field._UNDERSCORE_CONSTANTS with their public ClassProperty
equivalents in math_utils.py and geometry.py.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ument future work

- conftest.py: default --headless to True so tests don't open rsim window
- strategy_runner.py: skip ball teleport on STOP when next_command is
  BALL_PLACEMENT so the robot must physically carry the ball
- test_referee_rsim.py: replace broken full-sequence test with a comment
  documenting why it is deferred (ball placement carry mechanics not yet
  reliable in rsim)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Penalty and ball placement buttons are not in use; removing them keeps
the operator panel focused on the commands we actually use.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Ball placement phase before free kick per SSL rulebook
- BallPlacementOursStep carry mechanics investigation (two-robot kissing)
- GUI suggested next action to reduce operator cognitive load

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Delete dead RefereeStateMachine (never wired up, duplicated GameStateMachine
  with a broken _replace() call on a mutable class)
- Remove dead len(obs)==4 branch in _run_step; RSim always reads from ref_buffer
- Snapshot TeamInfo via copy.copy() in _generate_referee_data() to prevent
  score mutations retroactively corrupting stored RefereeRefiner records
- Update docs and stale docstrings accordingly
- Document TeamInfo frozen dataclass refactor as deferred follow-up work

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
- Fix CustomReferee.step() to reset rules only on actual command transition,
  not when the state machine ignores the violation due to the cooldown
- Remove dead STOP branch and unused my_team_is_yellow param from KeepOutRule
- Add last_status_message and last_next_command properties to RefereeRefiner;
  remove direct _referee_records[-1] access from StrategyRunner
- Fix designated_position type annotation: Tuple[float] → Tuple[float, float]
- Import BALL_KEEP_OUT_DISTANCE from referee_constants in test file instead
  of duplicating the literal

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
RefereeData.__eq__ excludes stage_time_left to avoid spurious
re-records, so the deduplication in add_new_referee_data() was
discarding every frame's updated countdown. The stage_time_left
property was reading from the stale cached record, causing the
terminal display to freeze (e.g. always showing 4:59).

Fix: track _latest_stage_time_left independently in refine(),
updated on every call before the dedup check, and return it
from the stage_time_left property.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ation loop

Action nodes were parking robots at 0.55 m from the ball while the
KeepOutRule fires at < 0.5 m. A small ball nudge during restart would
put defending robots inside the rule threshold, triggering another STOP
and looping indefinitely. Increasing the action node clearance to 0.8 m
gives enough buffer to absorb positioning inaccuracy.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Resolved conflicts in main.py, field.py, game_frame.py, math_utils.py,
strategy_runner.py, and test_runner_misconfig.py. Updated referee branch
code to use the new FieldDimensions-based Field API (removed ClassProperty
constants, added field_dims param to Field.__init__), fixed Vector3D→Vector2D
issues in actions.py for fpp controller compatibility, and updated test
assertions to match the increased BALL_KEEP_OUT_DISTANCE (0.55→0.8).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace from_field_bounds with from_field_dims in RefereeGeometry so
  goal/defense dimensions scale with the actual field, not hardcoded to
  STANDARD_FIELD_DIMS
- Fix Vector3D→Vector2D conversions in actions.py for fpp controller
  compatibility (DirectFreeOursStep, BallPlacementOursStep)
- Rename get_min_bounding_zone→get_min_bounding_req in point_cycle_strategy
  and wandering_strategy to match abstract base class
- Update test fixtures to use new Field(field_dims=...) constructor and
  correct BALL_KEEP_OUT_DISTANCE (0.8 m)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
_project_outside_circle was using a hardcoded (1, 0) fallback when a
robot sits exactly on the obstruction center (dist == 0), pushing it to
positive-x regardless of which half the team defends. Now _clear_to_legal_
positions passes an own-half-aware fallback so coincident robots are always
cleared toward their correct side.

Also fix test_all_robots_placed_on_own_half_* to track final target per
robot (dict keyed by robot_id) rather than a flat list, so the count
assertion is robust against the clearing pass re-issuing a move command.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
isaac0804 and others added 22 commits June 3, 2026 16:26
… GUI"

This reverts commit c5caa3459ea2fa9e9da262e47d6729dbbb0eb46b.
…top-left"

This reverts commit d922a78d20ee7a969c37105e33497936986cd5df.
- demo_ball_placement.py: colleague entry point wired with the Exhibition
  Road field (GREAT_EXHIBITION_FIELD_DIMS, 4 m × 3 m), CustomReferee
  pre-configured for out-of-bounds → ball placement cycles, and GUI on
  port 8080. Zero referee config work needed by the strategy developer.

- utama_core/strategy/examples/ball_placement_strategy.py: BallPlacementStep
  skeleton with detailed docstring guide (approach → capture → carry flow,
  relevant API pointers, dribbler usage). BallPlacementStrategy wires it
  into the referee override tree for 2v2 exhibition play.

- utama_core/tests/strategy_runner/test_ball_placement_rsim.py: three rsim
  integration tests covering the full placement contract — placer approaches
  ball, placer progresses toward designated_position, non-placer robots clear
  and hold outside BALL_KEEP_OUT_DISTANCE.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…rage

- Test 1: robot 0 now starts 1.5 m from the ball (was already within
  tolerance), checks distance decrease ≥ 0.8 m instead of threshold crossing
- Test 3: hardcode placer_id=0 from reset_field instead of re-deriving it
  with the same min(distance_to_ball) logic as the implementation (tautological);
  move ball to centre so robot 1 has room to reach 0.8 m clearance
- Test 4 (new): robot 1 starts closer to the ball than robot 0; asserts
  robot 1 approaches the ball and robot 0 does not — exercises placer
  selection with robot 0 as non-placer, which original tests never covered
- Module docstring: add Known Gap section explaining why has_ball carry
  phase transition is not tested end-to-end

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Right-click field canvas (with God Mode active) to issue
  BALL_PLACEMENT_YELLOW/BLUE directly to any target point
- Uses force_command() on the state machine to bypass the STOP-first
  guard, so the command takes effect immediately without operator
  needing to advance through STOP manually
- Add demo_ball_placement_real.py: single-robot rsim/real entry point
  with all auto-rules off, operator-driven via GUI

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ting

DirectFreeOursStep unit tests were asserting the robot targets the exact
ball position, but the new implementation targets an approach point behind
the ball (_APPROACH_OFFSET). Update assertions to check the target is
closer to the ball than the robot's start position.

The two rsim integration tests were relying on OutOfBoundsRule triggering
DIRECT_FREE via OOB detection, but the state machine now routes through
BALL_PLACEMENT first before DIRECT_FREE. Switch reset_field to inject the
command directly via force_command() so each test stays focused on its
subject (kicker approach / keep-out clearance) rather than OOB detection.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When the placer has the ball and needs to reorient toward the target,
use turn_on_spot (dribbling=True) instead of move — this offsets lateral
velocity so the robot orbits the ball contact point rather than spinning
around its own centre, reducing the chance the dribbler loses the ball.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- state_machine: manual STOP→BALL_PLACEMENT_* via set_command now uses
  _post_ball_placement_command (like auto-advance 1) instead of
  unconditionally setting next_command=NORMAL_START, so foul-restart is
  preserved through placement.
- state_machine: force_command sets next_command=NORMAL_START when
  forcing a BALL_PLACEMENT_* command so auto-advance 4 can fire.
- KickAfterDirectFreeStep: replace seen-first-restart guard with
  prev_command tracking; only kick on NORMAL_START that followed
  a DIRECT_FREE_* command, so the post-placement restart is never skipped.
- demo_ball_placement_real.py: mode="real" (was "rsim").

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Robot 0 was teleported onto the ball expecting has_ball to fire, but rsim
infrared doesn't trigger reliably. Start robot 0 at (-1.3, 0) instead —
behind the ball relative to the target — so the approach motion reduces
distance to target, making progress measurable without requiring has_ball.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ball_placement_and_kick_strategy: KickAfterDirectFreeStep was never
  ticked during DIRECT_FREE_* (the RefereeOverride subtree handles it),
  so _prev_command never reached a DIRECT_FREE value and the post-
  placement kick was always skipped.  Introduced _CommandTracker, a
  mutable container updated by BallPlacementAndKickStrategy.step()
  before every tree tick, so the transition is observable even when the
  strategy subtree is overridden.

- robot_info: when trusted_ir_robots is set, untrusted robots that did
  not appear in robot_responses (non-blocking real-mode polling can omit
  robots) were keeping their stale has_ball value.  Refiner now applies
  vision-proximity inference for all untrusted robots up-front, then
  overlays IR readings only for trusted robots that responded.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
go_to_ball was setting target_oren = robot.angle_to(ball), meaning
the robot approached with its front toward the ball. Since the
kicker and IR dribbler sensor are on the back, the robot now
approaches with back toward ball so the IR sensor triggers correctly.
PositionRefiner now accepts allowed_yellow_ids and allowed_blue_ids.
Any robot ID reported by SSL-Vision that is not in the allowlist is
dropped before it enters the game state, preventing stray detections
from permanently accumulating in friendly_robots/enemy_robots.

StrategyRunner derives the allowlists from yellow/blue vision_to_cmd_mapping
keys so the roster is implicitly defined by what is configured in gefr.py
(or any other runner) — no extra config needed.
…e-team mode

_validate_mapping_covers_game_frame was called after wait_until_game_valid
but observed_ids were already allowlist-filtered, making the check circular
and causing empty observed IDs in output. Removed the dead post-loop calls.

Blue allowlist is now only derived when opp_strategy is present; in
single-team mode blue robots must remain visible as enemies regardless of
whether a blue mapping was provided.
@isaac0804

Copy link
Copy Markdown
Contributor

Update — ready for review

Branch has been rebased onto latest main and all 539 tests pass.

What's in this PR (summary for reviewers)

  • Defence/keeper rewrite (defend_parameter, goalkeep, defense_utils): geometry is now read from game.field instead of hardcoded constants, so it works on any field size (standard, GERF, etc.). Keeper positions itself one robot-radius inside the goal line (keeper_x) rather than on the line itself. Defender side-selection (_choose_defender_side) dynamically picks the better post to cover based on shadow width. Shadow geometry extracted into shared helpers in defense_utils.
  • go_to_ball orientation flip: approach angle is now angle_to_ball + π so the robot arrives with its back (kicker/dribbler) facing the ball. Real-robot behaviour change — correct for back-mounted kicker hardware.
  • Roster filtering (strategy_runner, game_gater, position_refiner): vision detections are filtered to the roster defined by vision_to_cmd_mapping. Two bugs fixed today: circular coverage check removed (observed IDs were already allowlist-filtered, making the check vacuous); blue allowlist now only applied when opp_strategy is present (single-team mode must see blue robots as enemies).
  • SIGINT handling: graceful stop with "press again to force quit" message; live status display cleaned up properly.
  • step_once() public method on StrategyRunner: thin wrapper for deterministic test harnesses.

Note on go_to_ball orientation — any robot using this skill needs a back-mounted kicker. If a robot approaches front-first after this merge, this commit (9fe7852) is the cause.

Re: Fred's comment on game.ball optionalitygoalkeep now has an explicit if game.ball is None: return None guard at the top, which handles the typing contract correctly without requiring callers to check.

@energy-in-joles energy-in-joles left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

YOLO

@energy-in-joles
energy-in-joles marked this pull request as ready for review June 4, 2026 20:26
Copilot AI review requested due to automatic review settings June 4, 2026 20:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@energy-in-joles
energy-in-joles merged commit 6665cdd into main Jun 4, 2026
2 checks passed
@energy-in-joles
energy-in-joles deleted the fix/defence-algo branch June 4, 2026 20:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants