Skip to content

Pragnay/randomagents#292

Merged
mpragnay merged 40 commits into3.0_betafrom
pragnay/randomagents
Mar 7, 2026
Merged

Pragnay/randomagents#292
mpragnay merged 40 commits into3.0_betafrom
pragnay/randomagents

Conversation

@mpragnay
Copy link
Copy Markdown

Spawning logic added with a random number of agents and randomized dimensions
Integrated with the 2.0 goal sampling for initial goals
Collision and off-road checks during spawns

…nd removed everything else to prevent redundencies
…sampling logic for initial goals, configurable params for spawn settings
@mpragnay mpragnay marked this pull request as ready for review February 12, 2026 01:47
Copilot AI review requested due to automatic review settings February 12, 2026 01:47
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Feb 12, 2026

Greptile Overview

Greptile Summary

This PR implements random agent spawning with configurable dimensions and collision-free placement. Agents are spawned with randomized vehicle dimensions (width, length, height) on drivable lanes, with rejection sampling to prevent collisions and offroad placements. Initial goals are sampled using the 2.0 goal sampling logic.

Key Changes:

  • Added random_agents initialization mode with configurable agent counts per environment (min_agents_per_env, max_agents_per_env)
  • Implemented collision and offroad checking during spawn via check_spawn_collision and check_spawn_offroad functions
  • Added randomized vehicle dimensions via AgentSpawnSettings struct with configurable bounds
  • Removed max_controlled_agents parameter in favor of new spawning system
  • Integrated 2.0 goal sampling logic for initial goal generation

Critical Issues Found:

  • Runtime error in drive.py:279-280: undefined attributes self.min_spawn_agents and self.max_spawn_agents (should be min_agents_per_env and max_agents_per_env)
  • Logic error in drive.h:3484: incorrect cosine calculation using atan2f instead of vector magnitude
  • Logic error in drive.h:1304-1322: spawn position always updated even when all rejection attempts fail
  • Logic error in drive.h:2144-2146: respawn_agent respawns all agents instead of just the individual agent in random mode

Confidence Score: 1/5

  • This PR will cause immediate runtime failures and has critical logic errors
  • The undefined attribute references in drive.py will cause AttributeError on initialization. Additionally, critical logic bugs in spawn validation, respawn behavior, and goal sampling math will cause incorrect simulation behavior if the runtime errors are fixed
  • Pay close attention to pufferlib/ocean/drive/drive.py (runtime errors) and pufferlib/ocean/drive/drive.h (logic errors in spawn/respawn/goal sampling)

Important Files Changed

Filename Overview
pufferlib/ocean/drive/binding.c added random agent distribution logic across environments, removed max_controlled_agents parameter
pufferlib/ocean/drive/drive.c added debug config logging and random agent count support in demo mode
pufferlib/ocean/drive/drive.h major spawning logic implementation with collision/offroad checks, but contains critical bugs in respawn logic, spawn validation, and goal sampling math
pufferlib/ocean/drive/drive.py added Python bindings for random spawning parameters, but has undefined attribute references that will cause runtime errors
pufferlib/ocean/env_config.h added config parsing for spawn dimension parameters and init_mode string parsing

Sequence Diagram

sequenceDiagram
    participant Python as Drive.py
    participant Binding as binding.c
    participant Core as drive.h
    
    Python->>Binding: shared() with random_agents mode
    Binding->>Binding: Distribute agents across envs
    Binding-->>Python: agent_offsets, map_ids, env_count
    
    Python->>Binding: env_init() for each env
    Binding->>Core: Initialize Drive struct
    Core->>Core: load_map_binary()
    Core->>Core: set_active_agents()
    Core->>Core: spawn_agents_with_counts()
    
    loop For each agent
        Core->>Core: spawn_agent()
        Core->>Core: Random vehicle dimensions
        
        loop Rejection sampling (MAX_SPAWN_ATTEMPTS)
            Core->>Core: get_random_point_on_lane()
            Core->>Core: check_spawn_collision()
            Core->>Core: check_spawn_offroad()
            alt Valid spawn
                Core->>Core: break
            else Invalid
                Core->>Core: continue
            end
        end
        
        Core->>Core: Update agent position & state
    end
    
    Core->>Core: init_goal_positions()
    Core->>Core: sample_new_goal() for each agent
    Core-->>Python: Environment ready
Loading

Copy link
Copy Markdown

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

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

9 files reviewed, 6 comments

Edit Code Review Agent Settings | Greptile

Comment thread pufferlib/ocean/drive/drive.py Outdated
Comment thread pufferlib/ocean/drive/drive.h Outdated
Comment on lines +3484 to +3485
float mod_heading = atan2f(agent->heading_y, agent->heading_x);
float cos_theta = dot/(mod_to_pt * mod_heading);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

incorrect cosine calculation - mod_heading uses atan2f which returns an angle, not a magnitude

the cosine is already computed via the dot product formula: cos(theta) = dot / (|v1| * |v2|), where |heading| should be the magnitude (which is 1.0 for normalized heading vectors), not atan2f

Suggested change
float mod_heading = atan2f(agent->heading_y, agent->heading_x);
float cos_theta = dot/(mod_to_pt * mod_heading);
float mod_heading = sqrtf(agent->heading_x * agent->heading_x + agent->heading_y * agent->heading_y);
float cos_theta = dot/(mod_to_pt * mod_heading);

Comment thread pufferlib/ocean/drive/drive.h
Comment thread pufferlib/ocean/drive/drive.py
Comment thread pufferlib/ocean/drive/drive.h Outdated
Comment thread pufferlib/ocean/drive/drive.py
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds a new random_agents initialization mode to the Ocean Drive environment, enabling randomized per-environment agent counts and randomized vehicle dimensions/poses during spawning, with additional spawn-time collision/off-road rejection and integration with goal sampling.

Changes:

  • Extend INI/env config and Python/C bindings to support random_agents mode and spawn dimension parameters.
  • Implement random agent spawning logic in the C core, including collision/off-road checks and goal initialization behavior.
  • Update demo/visualization entrypoints and default drive.ini settings to exercise the new mode.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 13 comments.

Show a summary per file
File Description
pufferlib/ocean/env_config.h Adds new spawn/agent-count config fields and parses init_mode strings.
pufferlib/ocean/drive/visualize.c Wires spawn settings into visualization env init; randomizes agent counts in random mode.
pufferlib/ocean/drive/error.h Extends error types and adjusts error string formatting.
pufferlib/ocean/drive/drive.py Adds Python-side config for random agent counts/dimensions and passes through to bindings.
pufferlib/ocean/drive/drive.h Implements spawn rejection sampling, off-road checks, random init mode wiring, and goal sampling adjustments.
pufferlib/ocean/drive/drive.c Updates demo to use spawn settings and random agent counts; changes default weights/map.
pufferlib/ocean/drive/datatypes.h Adds free_agents helper to free an array of agents safely.
pufferlib/ocean/drive/binding.c Adds random agent-count partitioning in shared() and passes spawn settings into env init.
pufferlib/config/ocean/drive.ini Updates defaults to enable random_agents and sets spawn/agent-count parameters.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pufferlib/ocean/env_config.h
Comment thread pufferlib/ocean/drive/drive.py
Comment thread pufferlib/ocean/drive/binding.c Outdated
Comment thread pufferlib/ocean/drive/binding.c
Comment thread pufferlib/ocean/drive/drive.h Outdated
Comment thread pufferlib/ocean/drive/drive.h
Comment thread pufferlib/ocean/drive/drive.h Outdated
Comment thread pufferlib/ocean/drive/drive.h
Comment thread pufferlib/ocean/drive/visualize.c
Comment thread pufferlib/ocean/drive/drive.c
mpragnay and others added 5 commits February 11, 2026 21:15
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Changed counts to max supported by sim, max_controlled, num_created and removed everything else to prevent redundencies

* Fixed memory leaks
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 10 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pufferlib/ocean/drive/drive.py
Comment thread pufferlib/ocean/drive/binding.c
Comment thread pufferlib/ocean/drive/drive.h Outdated
// Design Choice(we don't have wide vehicles on roads)
if (spawn_width > spawn_length)
spawn_width = spawn_length;
float spawn_height = 1.5f; // Design Choice: Doesn't matter as we don't have flying cars in 2026
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

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

spawn_height is currently hardcoded to 1.5f, so the configured env->spawn_settings.h is ignored even though it’s parsed and passed through config/bindings. Use the configured spawn height (and keep the z-centering adjustment) so spawns match the INI/Python parameters.

Suggested change
float spawn_height = 1.5f; // Design Choice: Doesn't matter as we don't have flying cars in 2026
float spawn_height = spawn_settings.h;

Copilot uses AI. Check for mistakes.
Comment thread pufferlib/ocean/drive/drive.h
Comment thread pufferlib/ocean/drive/error.h
Comment thread pufferlib/ocean/drive/visualize.c
c_reset(&env);
c_render(&env);
Weights *weights = load_weights("resources/drive/puffer_drive_resampling_speed_lane.bin");
Weights *weights = load_weights("best_policy_with_reward_conditioning.bin");
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

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

load_weights("best_policy_with_reward_conditioning.bin") appears to reference a file that isn’t in the repo and also drops the resources/drive/ prefix used elsewhere. This breaks the demo by default unless the artifact is committed and the path is corrected.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Is this comment correct? It's true that I don't see this file

Comment thread pufferlib/ocean/drive/drive.py
Comment thread pufferlib/ocean/drive/drive.h Outdated
Comment thread pufferlib/ocean/drive/binding.c Outdated
// Ensure last env can still meet min_agents_per_env requirement
int upper = (remaining - max_agents_per_env < min_agents_per_env) ? remaining - min_agents_per_env
: max_agents_per_env;
count = min_agents_per_env + rand() % (upper - min_agents_per_env + 1);
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

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

The upper bound computation for count = min_agents_per_env + rand() % (upper - min_agents_per_env + 1) can yield upper < min_agents_per_env for infeasible combinations of remaining, min_agents_per_env, and max_agents_per_env (e.g. remaining=9, min=5, max=6), leading to modulo-by-zero/UB. Add a feasibility check and a fallback strategy (e.g. error out, relax the min for the final env, or adjust prior counts) before computing the modulo range.

Suggested change
count = min_agents_per_env + rand() % (upper - min_agents_per_env + 1);
if (upper < min_agents_per_env) {
/* Infeasible to satisfy min_agents_per_env for all envs with remaining agents.
* Fall back by relaxing the minimum for this env: fill up to max_agents_per_env
* (or all remaining if fewer). This avoids a zero/negative modulo range.
*/
count = remaining > max_agents_per_env ? max_agents_per_env : remaining;
} else {
count = min_agents_per_env + rand() % (upper - min_agents_per_env + 1);
}

Copilot uses AI. Check for mistakes.
Comment thread pufferlib/config/ocean/drive.ini Outdated
Comment on lines +29 to +30
reward_lane_align = 0
reward_lane_center = 0
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

can we revert this?

Comment thread pufferlib/config/ocean/drive.ini Outdated
; Determines the target distance to the new goal in the case of goal_behavior = generate_new_goals.
; Large numbers will select a goal point further away from the agent's current position.
min_goal_distance = 0.5
min_goal_distance = 5.0
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

can we revert this?

Comment thread pufferlib/config/ocean/drive.ini
Comment thread pufferlib/ocean/drive/drive.h Outdated
// Initialization modes
#define INIT_ALL_VALID 0
#define INIT_ONLY_CONTROLLABLE_AGENTS 1
#define RANDOM_AGENTS 2
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I think this is sort of a bad name, it implies all sorts of things that aren't just "we randomize the number of agents in the scene". It could be called INIT_VARIABLE_AGENT_NUMBER even if that's mouthy

Comment thread pufferlib/ocean/drive/drive.h Outdated
Comment on lines +2572 to +2574
if (env->init_mode == RANDOM_AGENTS) {
// TODO: Needs to be handled later if we want GOAL_RESPAWN for random agents
spawn_agents_with_counts(env);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

A little puzzled by this. Anytime we need to respawn a single agent we respawn the whole thing?

Comment thread pufferlib/ocean/drive/drive.h
Comment thread pufferlib/ocean/drive/drive.h Outdated
render = True
render_async = False # Render interval of below 50 might cause process starvation and slowness in training
render_interval = 250
render_interval = 1000
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

why change this?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Rendering was taking a really long amount of time even with async on so I just increased the interval

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

This only happens when max_agents is very high; previously, we were working with just 32 so it was not a problem

Comment thread pufferlib/ocean/drive/binding.c Outdated

env->num_agents = max_agents;
if (env->init_mode == INIT_VARIABLE_AGENT_NUMBER) {
env->spawn_settings.max_agents_in_sim = max_agents_per_env; // Random Agents only supports controlled agents
Copy link
Copy Markdown

@eugenevinitsky eugenevinitsky Mar 6, 2026

Choose a reason for hiding this comment

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

Random Agents is not a good name to use throughout the codebase for this feature, it can mean too many things

f"Please reduce num_maps, add more maps to {map_dir}, or set allow_fewer_maps=True."
)

self.use_all_maps = use_all_maps
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

i'm sort of struggling to see why these use_all_maps changes showed up in this particular PR

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

This was a tiny bug with debugging using drive.py

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

agent->log_velocity_y = (float *)malloc(array_size * sizeof(float));
agent->log_heading = (float *)malloc(array_size * sizeof(float));
agent->log_valid = (int *)malloc(array_size * sizeof(int));
allocate_agent_trajectories(agent);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I am a tiny bit puzzled why this was pulled into a function

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I didn't want to like bloat the code and the log trajectories was required for goal respawn behavior

return (s >= 0 && s <= 1 && t >= 0 && t <= 1);
}

static bool check_offroad(Drive *env, Agent *agent) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

did we not have an offroad check elsewhere we could reuse?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Oh I guess you just needed to reuse it in a few spots, good

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

No like, this needs to be reworked, cuz it's a good practice

if (check_line_intersection(corners[k], corners[next], start, end)) {

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

But yea, there is no single function for check_offroad

Comment on lines +1557 to +1562
float spawn_length = random_uniform(spawn_settings.min_l, spawn_settings.max_l);
float spawn_width = random_uniform(spawn_settings.min_w, spawn_settings.max_w);

// Design Choice(we don't have wide vehicles on roads)
if (spawn_width > spawn_length)
spawn_width = spawn_length;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

btw, this is sort of a biased sampler. It'll tend to sample a lot of squares. Just a heads up. This is pretty fast, so it's possible you can just rejection sample until you get a valid sample

@mpragnay mpragnay merged commit 3430228 into 3.0_beta Mar 7, 2026
10 checks passed
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.

3 participants