Conversation
* Fix evaluate's encode problem * Update yaml encoding
* MNT: README * MNT: fix scenario 0 balloon position info. #8
* Skip Monte Carlo balloon simulation for scenario 0 scenario 0 uses static balloons at fixed heights, but reset() ran a full Monte Carlo balloon-flight simulation every call and immediately overwrote the result with static positions. Move the Monte Carlo path into the non-scenario-0 branch and build the static balloon array directly via a new __generate_static_balloon_flights helper. The resulting _balloon_flights array is identical to before; only the wasted Monte Carlo run and its file writes are removed. Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com> * Add pytest to dev optional-dependencies The scenario 0 test suite lives in tests/; declare pytest in the dev extra so it installs via requirements-dev.txt and the suite can be run. Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com> --------- Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com>
vpython was imported at module scope in balloon_world.py and listed as a hard dependency, so importing the environment required vpython even for matplotlib rendering or render_mode=None (e.g. Colab). Move the import into the vpython branch of _render_frame and expose vpython via a `vpython` optional extra. Default installs and the matplotlib renderer are unaffected; `pip install -e ".[vpython]"` enables it. Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com>
* Add cleanup invariant tests AST-based static checks for redundant file.close() after with-open, dead class-level imports, drifted example agent_kwargs, and empty f-strings. No new dependencies beyond the standard library and PyYAML. Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com> * Remove dead code, redundant calls, and unused imports - evaluate.py: drop three redundant file.close() after with-open - example_agents.py: drop dead class-level get_initial_attitude import inside NavigationAgent (already imported at module scope) - balloon_world.py: drop unused rocketpy.Function and vpython.arrow imports; drop f-prefix from two f-strings without interpolation - example_eval_cfg.yaml: drop dummy arg3 unused by AttitudeRateControlAgent - envs/__init__.py: use explicit "as" re-export to satisfy F401 Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com> * Document how to run tests and ignore .ci-venv Add a Testing section to README pointing at "python -m unittest discover tests", and gitignore the throwaway venv name commonly used to install ruff locally. Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com> * Declare PyYAML as a dependency evaluate.py and the cleanup tests import yaml, but PyYAML was never declared in pyproject.toml dependencies and is not pulled in transitively. A fresh `pip install -r requirements.txt` therefore leaves `import yaml` failing with ModuleNotFoundError. Add pyyaml to the project dependencies. Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com> --------- Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com> Co-authored-by: ZuoRen Chen <180084773+zuorenchen@users.noreply.github.com>
* Move Monte Carlo output out of the package directory
__generate_balloon_flights passed MonteCarlo a filename inside the
installed package directory (BalloonPoppingGymEnv/envs/data/), so every
non-scenario-0 reset wrote balloon_sim.{inputs,outputs,errors}.txt
there. That fails in read-only / installed environments and pollutes
the package tree. Write to tempfile.gettempdir() instead; the env only
uses the in-memory simulate() results, so the persisted files are a
pure side effect whose location does not affect behavior.
Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com>
* Use a per-process Monte Carlo output filename
tempfile.gettempdir() is shared across OS users, so a fixed "balloon_sim"
name there can collide: on a shared host the second user's reset() hits
a PermissionError writing the first user's files. Suffix the name with
the process id so concurrent processes and users get distinct paths.
Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com>
---------
Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com>
* Add uv as the recommended environment setup path uv installs the pinned Python, creates the virtual environment, and installs locked dependencies in one step, lowering the setup barrier for contributors. Declare activerocketpy as a path dependency via [tool.uv.sources] so uv resolves the submodule, pin Python to 3.14 via .python-version, and commit uv.lock. The requirements.txt / pip path is unchanged and still works for users who do not use uv. Also drop the duplicate vpython entry left in [project.dependencies] by the PR #7 / #15 merge -- vpython is the optional extra only. Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com> * Switch the README uv setup to the standalone installer The uv steps said `pip install uv` then `uv sync`, but pip places the uv executable in a scripts directory that is frequently not on PATH, so the bare `uv` command then fails. Document uv's standalone installer instead, which puts `uv` on PATH, and add a troubleshooting note covering a stale shell, PATH, and the `python -m uv` fallback. Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com> --------- Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com>
Add GitHub issue form templates under .github/ISSUE_TEMPLATE: a bug report, a feature request / proposal, and a question form, plus a config that keeps blank issues enabled. Each form collects the context that otherwise has to be asked for in follow-up comments. Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com>
* Enhance BalloonPoppingEnv with trajectory logging and tidy up attribute in _init_ and reset * Add save_trajectory function to save balloon and rocket trajectories into json file. Call save_trajectories in evaluate_scenario. TODO: render_trajectory
* Enhance BalloonPoppingEnv with trajectory logging and tidy up attribute in _init_ and reset * Add save_trajectory function to save balloon and rocket trajectories into json file. Call save_trajectories in evaluate_scenario. TODO: render_trajectory * Add balloon_status to trajectories * ENH: pack results for leaderboard submission * Add time to trajectory log * Save trajectory file in UTC
* Add gust model configuration to environment parameters and implement gust handling in BalloonPoppingEnv (cherry picked from commit 9550377) * Refactor quasi-random gust model * Fix rng usage. run __create_environment() after env.reset --------- Co-authored-by: RickyRicato <rickywang44@gmail.com>
* Render every balloon in the vpython renderer _render_frame's vpython branch created a single sphere and positioned it from _balloon_states[0], so a scenario with N balloons showed only the first. Create one sphere per balloon and update them all, which matches the matplotlib branch that already slices over every balloon. Add tests/test_render.py, which mocks vpython and checks that a reset creates one sphere per balloon. Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com> * Assert the vpython renderer repositions every balloon The render test checked that num spheres are created but not that all of them are repositioned each frame, so a regression updating only one sphere would still pass. Add a vector() call-count assertion that pins the per-balloon update loop. Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com> * ENH: Add rocket and balloon colors to vpython rendering * Add vPython option to comment --------- Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com> Co-authored-by: zuorenchen <zuorenchen@m110.nthu.edu.tw>
* Add GitHub Actions CI workflow Set up .github/workflows/ci.yml with a lint job (ruff check and format check) and a test job that installs the project with its ActiveRocketPy submodule, runs the suite with coverage, and uploads a coverage report. CI runs on pushes to main, pull requests, and manual dispatch. The Codecov upload step stays dormant until a CODECOV_TOKEN secret is configured. Add tests/test_import_smoke.py to confirm the package and example agents import after an install, and declare pytest-cov as a dev dependency for coverage generation. Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com> * Bump CI actions off the deprecated Node 20 runtime actions/checkout@v4, actions/setup-python@v5 and actions/upload-artifact@v4 run on Node 20, which GitHub deprecates in June 2026. Move them to the current majors (v6, v6, v7), which run on Node 24. Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com> * Probe the simulation stack with find_spec in the smoke test The skip guard used importlib.import_module, which executes rocketpy and so cannot tell an installed-but-broken stack apart from an absent one -- test_import_example_agents would skip the import breakage it exists to catch. find_spec checks installation without importing, so the test runs and fails loudly when an installed stack is broken. Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com> --------- Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com>
* Add leaderboard information * Run formatter * Add release notes * Update release notes
* Update ActiveRocketPy submodule to add actuator classes * Update names to align with ActiveRocketPy actuator class updates * Bump ActiveRocketPy version after actuator module merged to main
* Fix lint error * Remove pytest on vpython.vector calls
Bring uv.lock in line with pyproject.toml and the pinned Python 3.14: - add the dev extra's pytest-cov (declared in pyproject but absent from the lock), which pulls in coverage - re-resolve platform markers for Python 3.14 (e.g. contextily and its deps are constrained to python_full_version < '3.14') Regenerated with `uv lock` (uv 0.11.14).
A new ruff release can change lint/format rules and break CI without any code change. Pin ruff==0.15.20 in the lint job for reproducibility.
Convert the diagnostic print() calls in the environment step()/close() and the evaluation entry point to module-level loggers, with no change to computed values, reward, or control flow: - balloon_world.py: add a module logger; the two step() termination prints become logger.info and the close() print becomes logger.debug. - evaluate.py: add a module logger; the completion/total-reward prints become logger.info with lazy %-style args, and the CLI block calls logging.basicConfig(stream=sys.stdout) so console output is preserved. - tests/test_logging.py: assert close() emits through the module logger and that both modules expose a correctly named logger. evaluate_scenario keeps returning (env, agent, scenario_parameters).
* test: add unit tests for balloon pop-detection geometry Cover the scoring core, which had no functional tests: - _segment_distance_squared_batch: parallel, intersecting, degenerate (point) segments, colinear endpoint clamping, and batch output shape. - _detect_pops: released-in-path pops, distant miss, ground/popped balloons skipped, inclusive radius boundary, and correct index mapping across mixed release states. Pure-numpy helpers; guarded with skipUnless like the other runtime tests. * test: cover moving-balloon pop detection (swept balloon segment) The existing _detect_pops tests use static balloons (previous == current position). Add cases where the balloon sweeps a segment during the timestep (the scenario #1 wind-drift case), exercising the swept balloon path: - a balloon whose sweep crosses the rocket path is popped even though both of its endpoints are 5 m from the path (a static check would miss it) - a swept closest approach within the radius is popped - a balloon that sweeps past outside the radius is not popped
…n check (#55) * BUG: normalize line endings and drop BOM in the evaluate.py submission check pack_for_submission compared the local evaluate.py against the copy on main by hashing raw bytes, so a CRLF or BOM-prefixed working tree (common on Windows) hashed differently from GitHub's LF-served file and warned that an unmodified evaluate.py should not be modified. Decode with utf-8-sig and normalize line endings before hashing, so CRLF, a BOM, and a trailing-newline difference no longer false-positive while real modifications are still detected. Addresses #35 (the reported Windows false-positive). The separate branch-skew case (evaluate.py legitimately differs between main and develop, e.g. the #51 logging change) is not covered here and needs its own decision. * Extract _normalized_md5 to module level and unit-test it The evaluate.py integrity check in pack_for_submission was a nested closure, so the BOM/line-ending normalization had no way to be tested on its own. Hoist it to a module-level helper (no behavior change, it captured nothing from the enclosing scope) and add tests/test_submission_md5.py. The tests pin both halves of #35: BOM, CRLF, classic-Mac CR, and trailing-newline variants all hash identically, so a Windows checkout no longer false-positives, while real edits (changed return, extra whitespace, added line, renamed function) still change the hash. Guarded with skipUnless like the other runtime tests, since utils imports the rocketpy stack at module top. * Normalize the evaluate.py checksum on raw bytes str.splitlines() splits on a superset of the physical line endings Python's tokenizer recognizes (form feed, NEL, U+2028), so an LF-to-form-feed swap changed the program while hashing the same, and decoding as UTF-8 raised on a valid non-UTF-8 source (a file with a coding: declaration), turning the integrity warning into a crash. Normalize on raw bytes instead: drop a UTF-8 BOM, fold CRLF and CR to LF, strip a single trailing newline. That keeps the BOM/CRLF false-report fix (#35) while catching a form-feed semantic edit and never crashing on non-UTF-8 source. Tests now gate on find_spec for rocketpy, so a renamed or removed helper fails loudly instead of skipping, plus form-feed and non-UTF-8 regression cases. Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com> --------- Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com>
* Add golden-master regression test for scenario #1 (issue #38) Mirror the scenario #0 regression guard for scenario #1. Scenario #1 runs a 100-flight Monte Carlo balloon ensemble through the ECMWF ensemble atmosphere on reset, so the balloons drift; the test therefore compares the downsampled balloon position trajectories in addition to the rocket trajectory and the popped-balloon count. The env reset+step loop is driven directly (not evaluate.py) to avoid the save_trajectories file writes and the submission-MD5 network call. Two seeded runs are bit-identical on the same machine; tolerances (rtol 3%, atol 1 m) absorb cross-platform float drift only. Row-count guards catch launch-failure or early-termination regressions that the overlap clip would otherwise mask. The test is opt-in via BPC_RUN_SLOW_TESTS (~30 s per run) so it does not bloat the default PR CI; the project has no pytest slow-marker infrastructure yet. A small downsampled baseline is committed with a regenerate script for deliberate physics updates. * ci: run the slow scenario regression tests in CI Set BPC_RUN_SLOW_TESTS=1 in the test job so the opt-in slow regression tests (the scenario #1 Monte Carlo golden master) run in PR CI, while local default runs stay fast. * test: harden the scenario #1 golden-master against false negatives Same false-negative paths as scenario #0, plus the balloon-specific ones: - post_launch_rocket_positions dropped every NaN row, so a rocket trajectory that went NaN after launch was silently truncated. It now takes the trajectory from the first finite row and raises on any non-finite row after launch. - Flight duration and the downsampled row counts used a 2% tolerance, which waved through a full second of early termination. Both are now absolute. - The rocket and balloon comparisons used numpy's additive atol + rtol*expected; they now apply a real max(1 m, 3%) floor, which a NaN also fails. - The balloon array was downsampled 1-in-10 in the balloon index, so 90 of the 100 balloons were never compared. It now keeps every balloon (time downsampling only) and the baseline regrows accordingly. - The class skipped on any ImportError. The skip now keys off whether rocketpy itself imports, the BalloonPoppingGymEnv imports fail loudly when the stack is present but broken, and with BPC_RUN_SLOW_TESTS set a missing stack fails rather than skips. Also pin the expected popped count (0), read the real scenario seed in the regenerator, handle a future truncated step, parse the slow-gate flag strictly, and drop the stale "does not bloat CI" note now that CI runs it. Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com> --------- Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com>
* test: add golden-master regression for scenario #0 (issue #38) Re-runs scenario #0 (deterministic: static balloons, fixed seed, ideal sensors, no Monte Carlo) with the default agent and compares against a committed baseline, guarding against unintended physics changes when ActiveRocketPy is updated. - Compares popped count (exact, == 10), rocket position trajectory (rtol 3%, atol 1 m), and flight duration (within 2%). - Baseline is downsampled post-launch positions; regenerate deliberately via tests/baselines/regenerate_scenario_0.py and review the diff. - Side-effect free runner; guarded with skipUnless like the other runtime tests. * test: harden the scenario #0 golden-master against false negatives Several ways the regression could stay green while the flight was actually broken: - post_launch_positions dropped every NaN row, not just the pre-launch ones, so a trajectory that went NaN after launch was silently truncated and slipped past the comparison. It now takes the trajectory from the first finite row and raises on any non-finite row after launch. - Flight duration and the downsampled row count used a 2% tolerance, which waved through a full second of early termination on a ~6000-step flight. Both are now absolute (a couple of steps, one row of cross-platform jitter). - The position comparison used numpy's additive atol + rtol*expected, so a small coordinate could drift by a metre plus another few percent. It now applies a real max(1 m, 3%) floor. - The class skipped on any ImportError, which hid the real breakage it exists to catch (a renamed or removed ActiveRocketPy symbol). The skip now keys off whether rocketpy itself imports; the BalloonPoppingGymEnv imports are left to fail loudly when the stack is present but broken. Also pin the expected popped count (10) so regenerating the baseline cannot bless a regression that pops fewer, handle a future truncated step, and read the real scenario seed in the regenerator instead of hardcoding it. Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com> --------- Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com>
Thread the scenario seed into the gyroscope / accelerometer / GNSS sensors via the new ActiveRocketPy sensor seed argument (#13). Each sensor gets a decorrelated sub-stream from SeedSequence([np_random_seed, domain]).spawn(3), clear of the balloon Monte Carlo seed tree, so the noisy-sensor observations are reproducible from the announced scenario seed and independent of the global RNG. Bumps the ActiveRocketPy submodule to develop (e9ebff65), which carries the seed support; adds tests/test_sensor_determinism.py. Closes #52.
Make the actuator tests catch a wrong sampling rate and cover all four actuators
…alloon-render-assertions
…er-assertions Put back the half of the render test #40 deleted, as behaviour
test: pin the coordinate frame the observations are reported in
…-gaps Cover the submission path #67 actually fixed, and pin the seed derivation
…-and-oss-conventions
…nventions Group the generated release notes by kind, and add CITATION.cff and SECURITY.md
…andles-truncation
Refuse a short balloon Monte Carlo instead of building a world out of it
…ncation The third runner still waited on terminated alone
Say what the isolation tests establish, and make the walker check it
Add a script that checks a submission's balloons against its seed
…and-changelog # Conflicts: # tests/baselines/regenerate_scenario_0.py # tests/baselines/regenerate_scenario_1.py
Add CONTRIBUTING.md and CHANGELOG.md, and make make format match CI
Two places that still read terminated from when that one flag also covered the clock. Splitting the two causes was right; neither of these moved with it. The log said "Terminated: Reached max time" for the case the flags now deliberately call truncation. That line is the one thing an operator reads to find out why a run stopped, so it should not name the wrong ending. The renderer drew a frame every 0.1 s of simulation time or when the episode ended, and the ending it checked was terminated alone. While that covered the clock, an episode running out of horizon always drew its last frame. Afterwards it only did so when the final step happened to land on the cadence. Scenario 0 truncates at step 9999 against a cadence of 10, so it did not, and scenario 1 ends this way normally. The existing console test pinned the old wording, so it moves too. Verified by mutation: both the old wording and the old render gate fail. The frame test also asserts that the final step misses the cadence, since otherwise the old gate would have drawn it anyway and the assertion would hold either way. Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com>
Sweeping for everything that still read terminated from when that one flag also covered the clock turned up two more than the code. The README described step() as returning "the new observations, reward, termination flag, and additional info", which is four values and one flag, and said the episode ends when the time limit is reached or the rocket lands, as though those were the same outcome. It is the document a competitor writes their loop from, so it was the worst place for this to be stale. It now names both flags, says which cause each one is, and shows the loop. The changelog had no entry at all. Added under Changed as breaking for every agent loop, alongside the observation copies, and the four behaviour fixes from this wave under Fixed. The README snippet is not a .py file, so the invariant test could not see it. It reads the fenced python blocks out of the README and puts any loop it finds through the same guard and unpack checks as the runners, plus a check that it found one at all, since a snippet that stops being tagged python would otherwise leave the check holding over nothing. Verified by mutation: the README showing the old loop, the README discarding truncated, and the snippet ceasing to be python all fail. Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com>
…-frame Finish the truncation change: the log, the final frame, the README and the changelog
|
Your order works. One step to add, and one thing I measured that says the rest is safe. TLDR: Step 2 is easy to miss. BPC pins ActiveRocketPy master ( On whether the bump moves anyone's numbers, I pointed a scratch worktree's submodule at the #19 branch and ran it:
That holds because both shipped scenarios leave On the cut-off: I would leave my six open PRs (#105 to #110) out of v0.1.0 Happy to run 3 and 4 once you have done 1 and 2, or to do the whole thing if you would rather. Step 2 is a release, so I would rather you called that one. |
Every merge on `develop` since v0.0.2 now appears. One of them is a scoring change and was the reason to check: #69 fixed the first interval after launch being skipped for pops, because the sweep started from an all-NaN position. Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com>
thc1006
left a comment
There was a problem hiding this comment.
Approving so this can go in.
Everything in the notes matches what is on develop: I checked each #nnn in the merge commits between main and develop against the changelog rather than reading for it.
One thing to land first, #111. The changelog was missing twenty of those pull requests, including a scoring change (#69, the first interval after launch was not checked for pops). It is a one file docs change, green, and without it v0.1.0 ships with an incomplete record.
My six open PRs (#105 to #110) are deliberately not in this cut. None of them changes a score, and the golden masters are unchanged by all six together, so there is no reason to hold the tag for them. They can go in v0.1.1 alongside the ActiveRocketPy bump.
Credit the twenty pull requests the changelog had left out
v0.1.0
Forty-seven pull requests since v0.0.2, from @zuorenchen, @thc1006 and @chichunwang.
Closes #93.
Urgent Upgrade Notes
Read this before running an existing agent
Running out of time is reported as
truncated, notterminated. Gymnasium keeps the two apart deliberately:terminatedmeans the episode reached a terminal state, which here is the flight ending, andtruncatedmeans something outside it stopped the episode, which is what running out of the precomputed horizon is. v0.0.2 returnedterminated=Truefor both, so a loop written aswhile not terminated:never ends on the horizon. It keeps callingstep(),current_stepwalks past the end of the precomputed balloon flights, and the run dies indexing them. For scenario 1 the horizon is the usual ending, so this is the common path rather than an edge.Any agent loop needs to become
while not (terminated or truncated):and keep the fourth valuestep()returns.evaluate.pyand both example runners indoc/examples/were updated with the change. The distinction also matters to a learning agent: an algorithm bootstraps the value of the final state when the episode was truncated and does not when it terminated, so reporting the clock as termination teaches it that running out of time is absorbing. (#94, #102, @thc1006), for #93Observations and
given_parametersare copies now. An agent that wrote into the arrays it was handed used to be writing into the environment's own state, which was a scoring hole rather than an API detail: writing2intoobservation["balloon_status"]and never launching returned the whole scenario. Both are detached, so an agent that relied on mutating them in place no longer affects anything. (#99, @thc1006)The gimbal limit parameter was renamed.
rocket.control.gimbal_rangeis nowrocket.control.max_gimbal_angle, and there is no alias. This key is in the whitelist agents receive, so an agent that readsgiven_parameters["rocket"]["control"]["gimbal_range"]raisesKeyErroron v0.1.0. The shipped example agents do not read it, so they are unaffected. (#39, @zuorenchen)The default renderer crashes on Matplotlib 3.11.
render_mode: "matplotlib"is whatexample_eval_cfg.yamlships with and what README describes as needing no extra. On Matplotlib 3.11 the first frame raisesAttributeError: 'list' object has no attribute 'shape'and the whole evaluation is lost. It needs two things at once, and this code produces both:Line3D.drawreads_verts3d[0].shapeonly when a coordinate is invalid for the axis scale, which every frame before launch is because the rocket state is all-NaN, and the update passed lists rather than arrays. Nothing bounds the version: the only constraint anywhere is ActiveRocketPy'smatplotlib>=3.9.0.uv syncresolves 3.10.9 from the lockfile and is unaffected, butpip install -r requirements-dev.txt, which is what CI and the README's pip path both use, resolves whatever is current and hits it. Filed as #89 and fixed in this release by #90. Kept here because anyone who ran v0.0.2 against a current pip install hit it, and because it explains why the version you get is not the version the lockfile records.The command line score line gained a prefix, and the notebook stopped printing one. Diagnostics moved from
printto theloggingmodule, and only the CLI configures logging.Total reward: 7now readsINFO:__main__:Total reward: 7, so anything reading that line by prefix breaks. The Colab notebook andrun_for_evaluationindoc/examples/run_env_agent.pyconfigure no logging at all, so they print neither the completion nor the score; the notebook's last cell renders the returned environment tuple where the score used to be. (#51, @thc1006). Fixed in this release by #88, so v0.1.0 prints the score the way v0.0.2 did. The note stays because a v0.0.2 user reading these lines from a script needs to know the format did move and came back.Trajectories differ from v0.0.2. The physics engine moved to the RocketPy v1.13 line and gained actuator dynamics. Results are reproducible within a release, not across this boundary, so anything measured against v0.0.2 needs re-running. (#60, @zuorenchen), (#39, @zuorenchen)
Changes by Kind
The first sixteen pull requests, grouped. Everything that landed after these
notes were first written is under Also in this release below.
Feature
gimbal_time_constant,roll_torque_time_constantandthrottle_time_constant; both shipped scenarios leave themnull, which keeps the previous pass-through behaviour. (#39, @zuorenchen)loggingmodule rather thanprint. (#51, @thc1006)Bug or Regression
pack_for_submissionraisedTypeErroron aSeedSequencesensor seed, which broke packing for every competitor. (#67, @thc1006)evaluate.pyintegrity check no longer throws away a submission that has already been simulated. (#68, @thc1006)AttributeError, and the interval between launch and the first simulated sample takes part in pop detection rather than being skipped. (#65, @thc1006)evaluate.pyintegrity check no longer reports a mismatch for a file that differs only by a byte-order mark or by line endings. (#55, @thc1006)matplotlibrenderer no longer crashes on Matplotlib 3.11.Line3D.drawreads a shape off the coordinates whenever one is invalid for the axis scale, which every frame before launch is, and the renderer was handing it lists. (#90, @thc1006)Total reward: Nagain rather thanINFO:__main__:Total reward: N, and the Colab notebook andrun_for_evaluationprint the score instead of nothing. (#88, @thc1006)Testing and CI
uv.lockis current against the ActiveRocketPy submodule, which is what later caught a stale lockfile ondevelop. (#62, @thc1006)uv.lockrelocked against the submodule. (#46, @thc1006)uv.lockrelocked after the version bump, and CI now also runs on pushes todevelopso a red integration branch cannot go unnoticed. (#78, @thc1006)Known limitations
Stated here rather than discovered later. None of these are new in v0.1.0.
0.366 * T, and two independent 15 degree axes reach a combined deflection of 21.47 degrees. Every entrant runs the same model, so this does not favour anyone, but v0.1.0 should not be described as a validated thrust-vector model. Tracked upstream as ActiveRocketPy#21.burn_timewhatever the throttle did, so a throttle time constant lags the thrust without lagging the mass schedule. Documented in the README and unchanged here.tvc,rollandthrottleare first read on the followingstep(). The flight is not advanced on the launch step either, so nothing is simulated with the dropped values, but the contract is worth stating explicitly.Also in this release
Everything below landed on
developafter these notes were first written. Eachone names the regression it makes visible, since every one was found by a
mutation the suite as shipped did not catch.
Changes what a competitor sees
2into the status array and never launching scored the whole scenario, and an agent could widen its own throttle range or change the rocket's moment of inertia from its constructor.evaluate.py./tmp, and keeps it when a run fails so there is something to read.Closes a hole in what the suite can see
.pyfile, so the invariant test could not see it; it reads the fenced blocks now.Tooling
scripts/verify_submission.py, which regenerates a submission's balloons from the shipped scenario and its seed and asks whether the claimed pops were anywhere the rocket says it went. It does not verify the rocket trajectory, which stays the competitor's claim.Still open, and therefore not in v0.1.0
Open questions rather than pending work: #57 on submission size, and #35 on the integrity warning, which fires for any checkout that is not current
mainand needs a release-pinned reference rather than a moving branch to close properly.Verified as a whole
Each of these was green on its own base, which says nothing about the merged
tree, so
developwas checked out clean and run through the exact CI after thelast of them landed: ruff check, ruff format,
uv lock --check, and 315 testswith 190 subtests passing with the slow suite enabled.
Contributors
Thank you to everyone whose work is in this release: @zuorenchen, @thc1006, @chichunwang.
Full Changelog: v0.0.2...v0.1.0