cuju-render 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cuju/render/__init__.py +759 -0
- cuju/render/__main__.py +1065 -0
- cuju/render/_export/__init__.py +3 -0
- cuju/render/_export/feed.py +542 -0
- cuju/render/_export/sizes.py +157 -0
- cuju/render/_export/video.py +1618 -0
- cuju/render/_export/video_jobs.py +408 -0
- cuju/render/_format/__init__.py +3 -0
- cuju/render/_format/annotations.py +235 -0
- cuju/render/_format/codec.py +307 -0
- cuju/render/_format/keep.py +121 -0
- cuju/render/_format/recording.py +860 -0
- cuju/render/_format/writer.py +54 -0
- cuju/render/_guide/__init__.py +3 -0
- cuju/render/_guide/doctor.py +523 -0
- cuju/render/_guide/help.py +62 -0
- cuju/render/_guide/tutorial.py +254 -0
- cuju/render/_http/__init__.py +3 -0
- cuju/render/_http/commands.py +57 -0
- cuju/render/_http/headers.py +103 -0
- cuju/render/_http/launch.py +500 -0
- cuju/render/_http/launchers.py +408 -0
- cuju/render/_http/pwa.py +128 -0
- cuju/render/_http/server.py +2105 -0
- cuju/render/_http/window.py +360 -0
- cuju/render/_moments/__init__.py +3 -0
- cuju/render/_moments/captions.py +284 -0
- cuju/render/_moments/events.py +1123 -0
- cuju/render/_moments/search.py +694 -0
- cuju/render/_packages/__init__.py +3 -0
- cuju/render/_packages/glsl.py +746 -0
- cuju/render/_packages/loudness.py +334 -0
- cuju/render/_packages/pack.py +1959 -0
- cuju/render/_packages/presentation.py +1376 -0
- cuju/render/_packages/vocabulary.py +609 -0
- cuju/render/_playing/__init__.py +3 -0
- cuju/render/_playing/achievements.py +463 -0
- cuju/render/_playing/bench.py +609 -0
- cuju/render/_playing/challenges.py +1813 -0
- cuju/render/_playing/demo.py +323 -0
- cuju/render/_playing/draw.py +211 -0
- cuju/render/_playing/errors.py +20 -0
- cuju/render/_playing/game.py +645 -0
- cuju/render/_playing/limits.py +339 -0
- cuju/render/_playing/play.py +926 -0
- cuju/render/_playing/policy_worker.py +250 -0
- cuju/render/_playing/strict.py +810 -0
- cuju/render/_registry/__init__.py +3 -0
- cuju/render/_registry/clips.py +394 -0
- cuju/render/_registry/joints.py +108 -0
- cuju/render/_registry/teams.py +2058 -0
- cuju/render/_store/__init__.py +3 -0
- cuju/render/_store/home.py +70 -0
- cuju/render/_store/library.py +604 -0
- cuju/render/_store/samples.py +163 -0
- cuju/render/_text/__init__.py +3 -0
- cuju/render/_text/languages.py +637 -0
- cuju/render/_text/messages.py +1396 -0
- cuju/render/_tournaments/__init__.py +3 -0
- cuju/render/_tournaments/results.py +547 -0
- cuju/render/_tournaments/tournament.py +2742 -0
- cuju/render/_tournaments/tournament_cli.py +587 -0
- cuju/render/_tournaments/tournament_runner.py +1975 -0
- cuju/render/_version.py +6 -0
- cuju/render/_watch/__init__.py +3 -0
- cuju/render/_watch/grid.py +265 -0
- cuju/render/_watch/live.py +664 -0
- cuju/render/_watch/notebook.py +79 -0
- cuju/render/_watch/show.py +171 -0
- cuju/render/samples/sample-11v11.cjr +0 -0
- cuju/render/samples/sample-5v5.cjr +0 -0
- cuju/render/samples/teams/starter/main.py +33 -0
- cuju/render/scenarios/benchmark_full_game_11_vs_11_easy.py +46 -0
- cuju/render/scenarios/benchmark_full_game_11_vs_11_hard.py +46 -0
- cuju/render/scenarios/benchmark_full_game_11_vs_11_medium.py +46 -0
- cuju/render/scenarios/benchmark_full_game_5_vs_5_easy.py +34 -0
- cuju/render/scenarios/benchmark_full_game_5_vs_5_hard.py +34 -0
- cuju/render/scenarios/benchmark_full_game_5_vs_5_medium.py +34 -0
- cuju/render/web/assets/adboard_polygon.webp +0 -0
- cuju/render/web/assets/adboard_your_ad_here.webp +0 -0
- cuju/render/web/assets/animations/kaykit-rig-medium-combatmelee.glb +0 -0
- cuju/render/web/assets/animations/kaykit-rig-medium-general.glb +0 -0
- cuju/render/web/assets/animations/kaykit-rig-medium-movementadvanced.glb +0 -0
- cuju/render/web/assets/animations/kaykit-rig-medium-movementbasic.glb +0 -0
- cuju/render/web/assets/animations/kaykit-rig-medium-simulation.glb +0 -0
- cuju/render/web/assets/animations/kaykit-rig-medium-special.glb +0 -0
- cuju/render/web/assets/ball.glb +0 -0
- cuju/render/web/assets/challenges/3_vs_1_with_keeper.mp4 +0 -0
- cuju/render/web/assets/challenges/3_vs_1_with_keeper.webp +0 -0
- cuju/render/web/assets/challenges/corner.mp4 +0 -0
- cuju/render/web/assets/challenges/corner.webp +0 -0
- cuju/render/web/assets/challenges/counterattack_easy.mp4 +0 -0
- cuju/render/web/assets/challenges/counterattack_easy.webp +0 -0
- cuju/render/web/assets/challenges/counterattack_hard.mp4 +0 -0
- cuju/render/web/assets/challenges/counterattack_hard.webp +0 -0
- cuju/render/web/assets/challenges/empty_goal.mp4 +0 -0
- cuju/render/web/assets/challenges/empty_goal.webp +0 -0
- cuju/render/web/assets/challenges/empty_goal_close.mp4 +0 -0
- cuju/render/web/assets/challenges/empty_goal_close.webp +0 -0
- cuju/render/web/assets/challenges/manifest.json +375 -0
- cuju/render/web/assets/challenges/one_on_one.mp4 +0 -0
- cuju/render/web/assets/challenges/one_on_one.webp +0 -0
- cuju/render/web/assets/challenges/one_on_one_hard.mp4 +0 -0
- cuju/render/web/assets/challenges/one_on_one_hard.webp +0 -0
- cuju/render/web/assets/challenges/pass_and_shoot_with_keeper.mp4 +0 -0
- cuju/render/web/assets/challenges/pass_and_shoot_with_keeper.webp +0 -0
- cuju/render/web/assets/challenges/run_pass_and_shoot_with_keeper.mp4 +0 -0
- cuju/render/web/assets/challenges/run_pass_and_shoot_with_keeper.webp +0 -0
- cuju/render/web/assets/challenges/run_to_score.mp4 +0 -0
- cuju/render/web/assets/challenges/run_to_score.webp +0 -0
- cuju/render/web/assets/challenges/run_to_score_with_keeper.mp4 +0 -0
- cuju/render/web/assets/challenges/run_to_score_with_keeper.webp +0 -0
- cuju/render/web/assets/challenges/single_goal_versus_lazy.mp4 +0 -0
- cuju/render/web/assets/challenges/single_goal_versus_lazy.webp +0 -0
- cuju/render/web/assets/crowd.webp +0 -0
- cuju/render/web/assets/crowd_normal.webp +0 -0
- cuju/render/web/assets/cuju-logo-384.png +0 -0
- cuju/render/web/assets/cuju-logo.png +0 -0
- cuju/render/web/assets/cuju-logo.svg +46 -0
- cuju/render/web/assets/cuju-mark-light.svg +33 -0
- cuju/render/web/assets/cuju-mark.svg +33 -0
- cuju/render/web/assets/goal.glb +0 -0
- cuju/render/web/assets/grass.webp +0 -0
- cuju/render/web/assets/hair_black.png +0 -0
- cuju/render/web/assets/hair_blonde.png +0 -0
- cuju/render/web/assets/hair_brown.png +0 -0
- cuju/render/web/assets/hair_darkblonde.png +0 -0
- cuju/render/web/assets/hair_long01.glb +0 -0
- cuju/render/web/assets/hair_long02.glb +0 -0
- cuju/render/web/assets/hair_medium01.glb +0 -0
- cuju/render/web/assets/hair_red.png +0 -0
- cuju/render/web/assets/hair_short01.glb +0 -0
- cuju/render/web/assets/hair_short02.glb +0 -0
- cuju/render/web/assets/icons/cuju-192.png +0 -0
- cuju/render/web/assets/icons/cuju-512.png +0 -0
- cuju/render/web/assets/icons/cuju-maskable-512.png +0 -0
- cuju/render/web/assets/kit_goalkeeper.webp +0 -0
- cuju/render/web/assets/kit_mask.png +0 -0
- cuju/render/web/assets/kit_normal.webp +0 -0
- cuju/render/web/assets/manifest.json +601 -0
- cuju/render/web/assets/models/kaykit-barbarian.glb +0 -0
- cuju/render/web/assets/models/kaykit-barbarian.json +23 -0
- cuju/render/web/assets/models/kaykit-knight.glb +0 -0
- cuju/render/web/assets/models/kaykit-knight.json +23 -0
- cuju/render/web/assets/models/kaykit-mage.glb +0 -0
- cuju/render/web/assets/models/kaykit-mage.json +23 -0
- cuju/render/web/assets/models/kaykit-mannequin-medium.glb +0 -0
- cuju/render/web/assets/models/kaykit-mannequin-medium.json +23 -0
- cuju/render/web/assets/models/kaykit-ranger.glb +0 -0
- cuju/render/web/assets/models/kaykit-ranger.json +23 -0
- cuju/render/web/assets/models/kaykit-rogue-hooded.glb +0 -0
- cuju/render/web/assets/models/kaykit-rogue-hooded.json +23 -0
- cuju/render/web/assets/models/kaykit-rogue.glb +0 -0
- cuju/render/web/assets/models/kaykit-rogue.json +23 -0
- cuju/render/web/assets/models/kaykit-skeleton-mage.glb +0 -0
- cuju/render/web/assets/models/kaykit-skeleton-mage.json +23 -0
- cuju/render/web/assets/models/kaykit-skeleton-minion.glb +0 -0
- cuju/render/web/assets/models/kaykit-skeleton-minion.json +23 -0
- cuju/render/web/assets/models/kaykit-skeleton-rogue.glb +0 -0
- cuju/render/web/assets/models/kaykit-skeleton-rogue.json +23 -0
- cuju/render/web/assets/models/kaykit-skeleton-warrior.glb +0 -0
- cuju/render/web/assets/models/kaykit-skeleton-warrior.json +23 -0
- cuju/render/web/assets/pitch.webp +0 -0
- cuju/render/web/assets/pitch_normal.webp +0 -0
- cuju/render/web/assets/player.glb +0 -0
- cuju/render/web/assets/shoe.webp +0 -0
- cuju/render/web/assets/shoe_sole.png +0 -0
- cuju/render/web/assets/skin_01.png +0 -0
- cuju/render/web/assets/skin_02.png +0 -0
- cuju/render/web/assets/skin_03.png +0 -0
- cuju/render/web/assets/skin_04.png +0 -0
- cuju/render/web/assets/sounds/applause.ogg +0 -0
- cuju/render/web/assets/sounds/beach-wave-1.ogg +0 -0
- cuju/render/web/assets/sounds/beach-wave-2.ogg +0 -0
- cuju/render/web/assets/sounds/beach-wave-3.ogg +0 -0
- cuju/render/web/assets/sounds/beach-wave-4.ogg +0 -0
- cuju/render/web/assets/sounds/city-horn.ogg +0 -0
- cuju/render/web/assets/sounds/city-loop.ogg +0 -0
- cuju/render/web/assets/sounds/city-siren.ogg +0 -0
- cuju/render/web/assets/sounds/crowd-cheer.ogg +0 -0
- cuju/render/web/assets/sounds/crowd-loop.ogg +0 -0
- cuju/render/web/assets/sounds/crowd-ooh.ogg +0 -0
- cuju/render/web/assets/sounds/gulls.ogg +0 -0
- cuju/render/web/assets/sounds/neon-sting.ogg +0 -0
- cuju/render/web/assets/sounds/rain-loop.ogg +0 -0
- cuju/render/web/assets/sounds/sounds.json +46 -0
- cuju/render/web/assets/sounds/space-beep.ogg +0 -0
- cuju/render/web/assets/sounds/space-hum.ogg +0 -0
- cuju/render/web/assets/sounds/space-radio.ogg +0 -0
- cuju/render/web/assets/sounds/whistle-long.ogg +0 -0
- cuju/render/web/assets/sounds/whistle-short.ogg +0 -0
- cuju/render/web/assets/sounds/wind-loop.ogg +0 -0
- cuju/render/web/easy.css +357 -0
- cuju/render/web/export.html +23 -0
- cuju/render/web/feed.css +262 -0
- cuju/render/web/feed.html +28 -0
- cuju/render/web/flat.css +120 -0
- cuju/render/web/home.css +837 -0
- cuju/render/web/index.html +373 -0
- cuju/render/web/js/boot.js +50 -0
- cuju/render/web/js/broadcast/audio.js +2141 -0
- cuju/render/web/js/broadcast/celebrate.js +1238 -0
- cuju/render/web/js/broadcast/decisions.js +609 -0
- cuju/render/web/js/broadcast/hud.js +352 -0
- cuju/render/web/js/broadcast/show.js +3614 -0
- cuju/render/web/js/broadcast/transition.js +2231 -0
- cuju/render/web/js/broadcast/voices.js +153 -0
- cuju/render/web/js/capture.js +101 -0
- cuju/render/web/js/core/accessibility.js +90 -0
- cuju/render/web/js/core/api.js +429 -0
- cuju/render/web/js/core/comfort.js +135 -0
- cuju/render/web/js/core/i18n.js +705 -0
- cuju/render/web/js/core/icons.js +167 -0
- cuju/render/web/js/core/input-settings.js +156 -0
- cuju/render/web/js/core/keys.js +277 -0
- cuju/render/web/js/core/language-ui.js +125 -0
- cuju/render/web/js/core/menu.js +853 -0
- cuju/render/web/js/core/names.js +113 -0
- cuju/render/web/js/core/page-structure.js +110 -0
- cuju/render/web/js/core/scenarios.js +109 -0
- cuju/render/web/js/core/spoilers.js +236 -0
- cuju/render/web/js/core/typeahead.js +38 -0
- cuju/render/web/js/export/dom-picture.js +330 -0
- cuju/render/web/js/export/export-host.js +97 -0
- cuju/render/web/js/export/feed-dialog.js +191 -0
- cuju/render/web/js/export/feed-params.js +266 -0
- cuju/render/web/js/export/feed.js +363 -0
- cuju/render/web/js/export/mp4-mux.js +295 -0
- cuju/render/web/js/export/photo-core.js +313 -0
- cuju/render/web/js/export/photo.js +1314 -0
- cuju/render/web/js/export/safe-frame.js +59 -0
- cuju/render/web/js/export/size-fields.js +103 -0
- cuju/render/web/js/export/sizes.js +178 -0
- cuju/render/web/js/export/video-boot.js +55 -0
- cuju/render/web/js/export/video-browser.js +325 -0
- cuju/render/web/js/export/video-export.js +417 -0
- cuju/render/web/js/export/video-plan.js +207 -0
- cuju/render/web/js/export/video-range.js +185 -0
- cuju/render/web/js/export/webm-mux.js +198 -0
- cuju/render/web/js/feed-boot.js +151 -0
- cuju/render/web/js/feed-frame.js +101 -0
- cuju/render/web/js/help/doctor.js +488 -0
- cuju/render/web/js/help/firstrun.js +379 -0
- cuju/render/web/js/help/help-center.js +438 -0
- cuju/render/web/js/help/install-offer.js +164 -0
- cuju/render/web/js/help/install-state.js +104 -0
- cuju/render/web/js/help/lessons-core.js +207 -0
- cuju/render/web/js/help/lessons.js +463 -0
- cuju/render/web/js/help/palette.js +584 -0
- cuju/render/web/js/help/pwa.js +257 -0
- cuju/render/web/js/help/tour-core.js +241 -0
- cuju/render/web/js/help/tour.js +461 -0
- cuju/render/web/js/main.js +2513 -0
- cuju/render/web/js/pages/draw.js +188 -0
- cuju/render/web/js/pages/fieldpage.js +530 -0
- cuju/render/web/js/pages/home.js +521 -0
- cuju/render/web/js/pages/matches.js +779 -0
- cuju/render/web/js/pages/moments-ui.js +359 -0
- cuju/render/web/js/pages/queue-filters.js +63 -0
- cuju/render/web/js/pages/recording-groups.js +80 -0
- cuju/render/web/js/pages/recordings.js +1069 -0
- cuju/render/web/js/play/bench.js +347 -0
- cuju/render/web/js/play/challenges.js +1302 -0
- cuju/render/web/js/play/medals.js +127 -0
- cuju/render/web/js/play/play.js +1953 -0
- cuju/render/web/js/play/teasers.js +196 -0
- cuju/render/web/js/play/touch.js +350 -0
- cuju/render/web/js/play/tournament-layout.js +487 -0
- cuju/render/web/js/play/tournament-results.js +441 -0
- cuju/render/web/js/play/tournament-strict.js +560 -0
- cuju/render/web/js/play/tournament.js +2585 -0
- cuju/render/web/js/players/animate.js +972 -0
- cuju/render/web/js/players/clips.js +224 -0
- cuju/render/web/js/players/kaykit.js +162 -0
- cuju/render/web/js/players/kits.js +676 -0
- cuju/render/web/js/players/number-patch.js +82 -0
- cuju/render/web/js/players/players.js +2694 -0
- cuju/render/web/js/players/recolor.js +893 -0
- cuju/render/web/js/players/retarget.js +325 -0
- cuju/render/web/js/players/rig-numbers.js +174 -0
- cuju/render/web/js/presentation/animator.js +231 -0
- cuju/render/web/js/presentation/assign.js +85 -0
- cuju/render/web/js/presentation/behaviors.js +440 -0
- cuju/render/web/js/presentation/budget.js +209 -0
- cuju/render/web/js/presentation/catalog.js +1674 -0
- cuju/render/web/js/presentation/clocks.js +72 -0
- cuju/render/web/js/presentation/commands.js +301 -0
- cuju/render/web/js/presentation/contract.js +240 -0
- cuju/render/web/js/presentation/effects.js +904 -0
- cuju/render/web/js/presentation/flash.js +605 -0
- cuju/render/web/js/presentation/freeze-worklet.js +22 -0
- cuju/render/web/js/presentation/glsl.js +464 -0
- cuju/render/web/js/presentation/host.js +323 -0
- cuju/render/web/js/presentation/index.js +2066 -0
- cuju/render/web/js/presentation/loudness-worklet.js +235 -0
- cuju/render/web/js/presentation/loudness.js +740 -0
- cuju/render/web/js/presentation/moments.js +536 -0
- cuju/render/web/js/presentation/post.js +327 -0
- cuju/render/web/js/presentation/preview.js +180 -0
- cuju/render/web/js/presentation/protocol.js +174 -0
- cuju/render/web/js/presentation/reach.js +158 -0
- cuju/render/web/js/presentation/rigging.js +130 -0
- cuju/render/web/js/presentation/sandbox.js +157 -0
- cuju/render/web/js/presentation/shaders.js +235 -0
- cuju/render/web/js/presentation/stage.js +402 -0
- cuju/render/web/js/presentation/teamaudio.js +722 -0
- cuju/render/web/js/presentation/tracks.js +197 -0
- cuju/render/web/js/presentation/values.js +81 -0
- cuju/render/web/js/recording/annotation-data.js +203 -0
- cuju/render/web/js/recording/captions.js +491 -0
- cuju/render/web/js/recording/codec.js +185 -0
- cuju/render/web/js/recording/decode-worker.js +70 -0
- cuju/render/web/js/recording/decode.js +94 -0
- cuju/render/web/js/recording/episodes.js +46 -0
- cuju/render/web/js/recording/format.js +461 -0
- cuju/render/web/js/recording/live.js +348 -0
- cuju/render/web/js/recording/moments-query.js +59 -0
- cuju/render/web/js/recording/moments.js +149 -0
- cuju/render/web/js/recording/pose.js +86 -0
- cuju/render/web/js/recording/timeline.js +275 -0
- cuju/render/web/js/teams/art.js +50 -0
- cuju/render/web/js/teams/clip-editor.js +293 -0
- cuju/render/web/js/teams/editor.js +1979 -0
- cuju/render/web/js/teams/library.js +344 -0
- cuju/render/web/js/teams/painter.js +633 -0
- cuju/render/web/js/teams/surprise-ui.js +195 -0
- cuju/render/web/js/teams/surprise.js +497 -0
- cuju/render/web/js/teams/team-check.js +127 -0
- cuju/render/web/js/watch/annotations.js +270 -0
- cuju/render/web/js/watch/flat.js +366 -0
- cuju/render/web/js/watch/grid.js +436 -0
- cuju/render/web/js/watch/pitch2d.js +289 -0
- cuju/render/web/js/watch/player-tools.js +192 -0
- cuju/render/web/js/watch/presets.js +201 -0
- cuju/render/web/js/world/arenas.js +997 -0
- cuju/render/web/js/world/ball.js +204 -0
- cuju/render/web/js/world/cameras.js +982 -0
- cuju/render/web/js/world/crowd.js +376 -0
- cuju/render/web/js/world/field.js +227 -0
- cuju/render/web/js/world/framing.js +145 -0
- cuju/render/web/js/world/occlusion.js +48 -0
- cuju/render/web/js/world/pitch.js +703 -0
- cuju/render/web/js/world/scene.js +395 -0
- cuju/render/web/js/world/sky.js +325 -0
- cuju/render/web/js/world/stadium.js +1262 -0
- cuju/render/web/js/world/vendor-sky.js +367 -0
- cuju/render/web/js/world/weather.js +264 -0
- cuju/render/web/languages/en.json +4750 -0
- cuju/render/web/lessons.json +137 -0
- cuju/render/web/manifest.webmanifest +23 -0
- cuju/render/web/style.css +8399 -0
- cuju/render/web/sw.js +235 -0
- cuju/render/web/vendor/fabric/LICENSE +16 -0
- cuju/render/web/vendor/fabric/fabric.min.mjs +1 -0
- cuju/render/web/vendor/lucide/LICENSE +43 -0
- cuju/render/web/vendor/lucide/icons.js +141 -0
- cuju/render/web/vendor/three/LICENSE +21 -0
- cuju/render/web/vendor/three/build/three.core.js +60586 -0
- cuju/render/web/vendor/three/build/three.module.js +19719 -0
- cuju/render/web/vendor/three/examples/jsm/loaders/GLTFLoader.js +4925 -0
- cuju/render/web/vendor/three/examples/jsm/postprocessing/EffectComposer.js +365 -0
- cuju/render/web/vendor/three/examples/jsm/postprocessing/MaskPass.js +195 -0
- cuju/render/web/vendor/three/examples/jsm/postprocessing/OutputPass.js +148 -0
- cuju/render/web/vendor/three/examples/jsm/postprocessing/Pass.js +191 -0
- cuju/render/web/vendor/three/examples/jsm/postprocessing/RenderPass.js +193 -0
- cuju/render/web/vendor/three/examples/jsm/postprocessing/ShaderPass.js +135 -0
- cuju/render/web/vendor/three/examples/jsm/postprocessing/UnrealBloomPass.js +541 -0
- cuju/render/web/vendor/three/examples/jsm/shaders/CopyShader.js +52 -0
- cuju/render/web/vendor/three/examples/jsm/shaders/LuminosityHighPassShader.js +68 -0
- cuju/render/web/vendor/three/examples/jsm/shaders/OutputShader.js +103 -0
- cuju/render/web/vendor/three/examples/jsm/utils/BufferGeometryUtils.js +1501 -0
- cuju/render/web/vendor/three/examples/jsm/utils/SkeletonUtils.js +496 -0
- cuju_render-0.1.0.dist-info/METADATA +116 -0
- cuju_render-0.1.0.dist-info/RECORD +382 -0
- cuju_render-0.1.0.dist-info/WHEEL +4 -0
- cuju_render-0.1.0.dist-info/licenses/LICENSE +202 -0
- cuju_render-0.1.0.dist-info/licenses/LICENSES/CC-BY-4.0.txt +396 -0
- cuju_render-0.1.0.dist-info/licenses/LICENSES/CC0-1.0.txt +127 -0
- cuju_render-0.1.0.dist-info/licenses/LICENSES/ISC.txt +16 -0
- cuju_render-0.1.0.dist-info/licenses/LICENSES/MIT.txt +24 -0
- cuju_render-0.1.0.dist-info/licenses/LICENSES/Unlicense.txt +13 -0
- cuju_render-0.1.0.dist-info/licenses/NOTICE +35 -0
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
# Copyright 2026 The cuju Authors
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
"""Codes the frames of a recording of version 2, which decode to the bytes of version 1 exactly.
|
|
5
|
+
|
|
6
|
+
The frames section stores every field of every frame as a lane, the values of one field across all
|
|
7
|
+
frames, and lanes of a kind lie together as planes of bytes, the low bytes of every lane and then
|
|
8
|
+
the high bytes. A joint is stored relative to its parent, as the rotation and offset that the
|
|
9
|
+
parent carries it by and a small residual, so most lanes change slowly and the whole file's gzip
|
|
10
|
+
finds them. The coding starts afresh at every keyframe, so any frame decodes from the keyframe
|
|
11
|
+
before it. The layout is in the repository's `docs/rendering.md`, under Recording format.
|
|
12
|
+
|
|
13
|
+
Speed: the coder works on whole lanes with numpy, every joint of every frame at once, and the
|
|
14
|
+
decoder one joint at a time over every frame, so a full match of eleven a side codes in a few
|
|
15
|
+
seconds and decodes in about one.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import numpy as np
|
|
21
|
+
|
|
22
|
+
# The steps between keyframes, ten seconds of play.
|
|
23
|
+
KEYFRAME_INTERVAL = 100
|
|
24
|
+
# A rotation component is stored in steps of 1 / UNIT, as version 1 stores it.
|
|
25
|
+
UNIT = 32767
|
|
26
|
+
HEAD_WORDS = 19
|
|
27
|
+
HEAD_BYTES = HEAD_WORDS * 4
|
|
28
|
+
JOINTS = 14
|
|
29
|
+
PARENTS = (-1, 0, 1, 2, 2, 4, 2, 6, 1, 8, 9, 1, 11, 12)
|
|
30
|
+
_CHILDREN = JOINTS - 1
|
|
31
|
+
_PARENT_INDEX = np.array(PARENTS[1:])
|
|
32
|
+
# The kinds of int16 lanes per player, in the order of the section, with the number of each.
|
|
33
|
+
_ROOT_POSITION, _ROOT_ROTATION = 3, 4
|
|
34
|
+
_LOCAL_ROTATION, _LOCAL_OFFSET = 3 * _CHILDREN, 3 * _CHILDREN
|
|
35
|
+
_ROTATION_RESIDUAL, _POSITION_RESIDUAL = 4 * _CHILDREN, 3 * _CHILDREN
|
|
36
|
+
_SIGNS = _CHILDREN
|
|
37
|
+
_CONJUGATE = np.array([-1, -1, -1, 1])
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def padded(players: int) -> int:
|
|
41
|
+
"""Returns the bytes of each of the three byte runs that close a frame of version 1."""
|
|
42
|
+
return -(-players // 4) * 4
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def frame_bytes(players: int) -> int:
|
|
46
|
+
"""Returns the bytes of one decoded frame, as version 1 stores it."""
|
|
47
|
+
return HEAD_BYTES + players * JOINTS * 14 + 3 * padded(players)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def section_bytes(players: int, count: int) -> int:
|
|
51
|
+
"""Returns the bytes of the frames section of `count` frames of `players` players."""
|
|
52
|
+
short = players * (
|
|
53
|
+
_ROOT_POSITION
|
|
54
|
+
+ _ROOT_ROTATION
|
|
55
|
+
+ _LOCAL_ROTATION
|
|
56
|
+
+ _LOCAL_OFFSET
|
|
57
|
+
+ _ROTATION_RESIDUAL
|
|
58
|
+
+ _POSITION_RESIDUAL
|
|
59
|
+
)
|
|
60
|
+
return count * (HEAD_BYTES + 2 * short + players * _SIGNS + 3 * padded(players))
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _hamilton(first: np.ndarray, second: np.ndarray) -> np.ndarray:
|
|
64
|
+
"""Returns the Hamilton products of quaternions, x, y, z and w along the first axis."""
|
|
65
|
+
x1, y1, z1, w1 = first
|
|
66
|
+
x2, y2, z2, w2 = second
|
|
67
|
+
return np.stack(
|
|
68
|
+
(
|
|
69
|
+
w1 * x2 + x1 * w2 + y1 * z2 - z1 * y2,
|
|
70
|
+
w1 * y2 + y1 * w2 + z1 * x2 - x1 * z2,
|
|
71
|
+
w1 * z2 + z1 * w2 + x1 * y2 - y1 * x2,
|
|
72
|
+
w1 * w2 - x1 * x2 - y1 * y2 - z1 * z2,
|
|
73
|
+
)
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _cross(first: np.ndarray, second: np.ndarray) -> np.ndarray:
|
|
78
|
+
"""Returns the cross products of vectors, x, y and z along the first axis."""
|
|
79
|
+
x1, y1, z1 = first
|
|
80
|
+
x2, y2, z2 = second
|
|
81
|
+
return np.stack((y1 * z2 - z1 * y2, z1 * x2 - x1 * z2, x1 * y2 - y1 * x2))
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _isqrt(values: np.ndarray) -> np.ndarray:
|
|
85
|
+
"""Returns the integer square roots of non-negative integers below 2**52."""
|
|
86
|
+
root = np.floor(np.sqrt(values.astype(np.float64))).astype(np.int64)
|
|
87
|
+
root -= root * root > values
|
|
88
|
+
root += (root + 1) * (root + 1) <= values
|
|
89
|
+
return root
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _predict_rotation(parent: np.ndarray, local: np.ndarray, sign: np.ndarray) -> np.ndarray:
|
|
93
|
+
"""Returns the rotation that `parent` carries the local rotation `local` to.
|
|
94
|
+
|
|
95
|
+
The local rotation gives x, y and z, its w follows from the unit length, negative where `sign`
|
|
96
|
+
is set, and the product rounds half up in steps of 1 / UNIT. Every value is an integer below
|
|
97
|
+
2**53, so a reader in double precision computes the same. Components lie along the first axis.
|
|
98
|
+
"""
|
|
99
|
+
w = _isqrt(np.maximum(UNIT * UNIT - (local * local).sum(0), 0))
|
|
100
|
+
w = np.where(sign != 0, -w, w)
|
|
101
|
+
product = _hamilton(parent, np.concatenate((local, w[None]), 0))
|
|
102
|
+
return (2 * product + UNIT) // (2 * UNIT)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _predict_position(origin: np.ndarray, parent: np.ndarray, offset: np.ndarray) -> np.ndarray:
|
|
106
|
+
"""Returns `origin` plus `offset` turned by the rotation `parent`, rounded half up."""
|
|
107
|
+
axis, w = parent[:3], parent[3:]
|
|
108
|
+
turned = _cross(axis, offset)
|
|
109
|
+
total = offset * (UNIT * UNIT) + 2 * w * turned + 2 * _cross(axis, turned)
|
|
110
|
+
return origin + (2 * total + UNIT * UNIT) // (2 * UNIT * UNIT)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _difference(values: np.ndarray, interval: int) -> np.ndarray:
|
|
114
|
+
"""Returns each value less the one before along the last axis, the keyframes as they are."""
|
|
115
|
+
out = values.copy()
|
|
116
|
+
out[..., 1:] -= values[..., :-1]
|
|
117
|
+
out[..., ::interval] = values[..., ::interval]
|
|
118
|
+
return out
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _sum(values: np.ndarray, interval: int) -> np.ndarray:
|
|
122
|
+
"""Undoes `_difference`, summing from each keyframe on."""
|
|
123
|
+
out = values.copy()
|
|
124
|
+
for start in range(0, values.shape[-1], interval):
|
|
125
|
+
part = out[..., start : start + interval]
|
|
126
|
+
np.cumsum(part, axis=-1, out=part)
|
|
127
|
+
return out
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _xor(values: np.ndarray, interval: int) -> np.ndarray:
|
|
131
|
+
"""Returns each byte XOR the one before along the last axis, the keyframes as they are."""
|
|
132
|
+
out = values.copy()
|
|
133
|
+
out[..., 1:] ^= values[..., :-1]
|
|
134
|
+
out[..., ::interval] = values[..., ::interval]
|
|
135
|
+
return out
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _unxor(values: np.ndarray, interval: int) -> np.ndarray:
|
|
139
|
+
"""Undoes `_xor`, from each keyframe on."""
|
|
140
|
+
out = values.copy()
|
|
141
|
+
for start in range(0, values.shape[-1], interval):
|
|
142
|
+
part = out[..., start : start + interval]
|
|
143
|
+
np.bitwise_xor.accumulate(part, axis=-1, out=part)
|
|
144
|
+
return out
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _steady(values: np.ndarray, interval: int, reach: int) -> np.ndarray:
|
|
148
|
+
"""Holds each value at the one before while it moves by at most `reach`, from each keyframe.
|
|
149
|
+
|
|
150
|
+
A held value costs nothing in its lane, and the residual takes up the difference.
|
|
151
|
+
"""
|
|
152
|
+
out = np.moveaxis(values, -1, 0).copy()
|
|
153
|
+
for frame in range(1, len(out)):
|
|
154
|
+
if frame % interval:
|
|
155
|
+
near = np.abs(out[frame] - out[frame - 1]) <= reach
|
|
156
|
+
out[frame] = np.where(near, out[frame - 1], out[frame])
|
|
157
|
+
return np.ascontiguousarray(np.moveaxis(out, 0, -1))
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _planes(values: np.ndarray, width: int) -> bytes:
|
|
161
|
+
"""Returns lanes `[..., frames]` of signed integers as zigzag planes, the lowest byte first."""
|
|
162
|
+
signed = np.ascontiguousarray(values).astype(f'<i{width}')
|
|
163
|
+
zigzag = ((signed << 1) ^ (signed >> (8 * width - 1))).view(f'<u{width}')
|
|
164
|
+
split = zigzag.reshape(-1).view(np.uint8).reshape(-1, width)
|
|
165
|
+
return b''.join(split[:, plane].tobytes() for plane in range(width))
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _unplanes(data: np.ndarray, width: int) -> np.ndarray:
|
|
169
|
+
"""Undoes `_planes` for planes `[width, lanes, frames]`, returning `[lanes, frames]` int64."""
|
|
170
|
+
joined = np.empty((*data.shape[1:], width), np.uint8)
|
|
171
|
+
for plane in range(width):
|
|
172
|
+
joined[..., plane] = data[plane]
|
|
173
|
+
zigzag = joined.view(f'<u{width}')[..., 0]
|
|
174
|
+
signed = (zigzag >> 1).astype(f'<i{width}') ^ -(zigzag & 1).astype(f'<i{width}')
|
|
175
|
+
return signed.astype(np.int64)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def encode(frames: list[bytes], players: int, interval: int = KEYFRAME_INTERVAL) -> bytes:
|
|
179
|
+
"""Returns the frames section of version 2 for frames of version 1.
|
|
180
|
+
|
|
181
|
+
Args:
|
|
182
|
+
frames: The frames, each as version 1 stores it.
|
|
183
|
+
players: The number of players.
|
|
184
|
+
interval: The frames from one keyframe to the next.
|
|
185
|
+
"""
|
|
186
|
+
count = len(frames)
|
|
187
|
+
size = frame_bytes(players)
|
|
188
|
+
raw = np.frombuffer(b''.join(frames), np.uint8).reshape(count, size)
|
|
189
|
+
nodes_end = HEAD_BYTES + players * JOINTS * 14
|
|
190
|
+
head = raw[:, :HEAD_BYTES].copy().view('<i4').T.astype(np.int64)
|
|
191
|
+
tail = np.ascontiguousarray(raw[:, nodes_end:].T)
|
|
192
|
+
nodes = raw[:, HEAD_BYTES:nodes_end].copy().view('<i2').reshape(count, players, JOINTS, 7)
|
|
193
|
+
# Components, joints, players and frames, so each lane of values is contiguous.
|
|
194
|
+
nodes = np.ascontiguousarray(nodes.transpose(3, 2, 1, 0)).astype(np.int64)
|
|
195
|
+
position, rotation = nodes[:3], nodes[3:]
|
|
196
|
+
parent, child = rotation[:, _PARENT_INDEX], rotation[:, 1:]
|
|
197
|
+
origin, place = position[:, _PARENT_INDEX], position[:, 1:]
|
|
198
|
+
conjugate = parent * _CONJUGATE[:, None, None, None]
|
|
199
|
+
# The local rotation, from the parent's rotation to the child's, in steps of 1 / UNIT.
|
|
200
|
+
local = _hamilton(conjugate, child)
|
|
201
|
+
turn = np.clip(np.rint(local[:3] / UNIT), -UNIT, UNIT).astype(np.int64)
|
|
202
|
+
turn = _steady(turn, interval, 1)
|
|
203
|
+
sign = (local[3] < 0).astype(np.uint8)
|
|
204
|
+
# The offset of the child in the frame of its parent, in the steps of a position.
|
|
205
|
+
unit = conjugate.astype(np.float64)
|
|
206
|
+
unit /= np.maximum(np.sqrt((unit * unit).sum(0)), 1.0)
|
|
207
|
+
gap = (place - origin).astype(np.float64)
|
|
208
|
+
turned = _cross(unit[:3], gap)
|
|
209
|
+
offset = gap + 2 * unit[3:] * turned + 2 * _cross(unit[:3], turned)
|
|
210
|
+
offset = _steady(np.clip(np.rint(offset), -UNIT, UNIT).astype(np.int64), interval, 1)
|
|
211
|
+
rotation_residual = child - _predict_rotation(parent, turn, sign)
|
|
212
|
+
position_residual = place - _predict_position(origin, parent, offset)
|
|
213
|
+
|
|
214
|
+
def lanes(values: np.ndarray) -> np.ndarray:
|
|
215
|
+
# Components, joints, players and frames become joints, components, players and frames.
|
|
216
|
+
return values.transpose(1, 0, 2, 3)
|
|
217
|
+
|
|
218
|
+
parts = [
|
|
219
|
+
_planes(_difference(head, interval), 4),
|
|
220
|
+
_planes(_difference(_difference(position[:, 0], interval), interval), 2),
|
|
221
|
+
_planes(_difference(rotation[:, 0], interval), 2),
|
|
222
|
+
_planes(lanes(_difference(turn, interval)), 2),
|
|
223
|
+
_xor(sign, interval).tobytes(),
|
|
224
|
+
_planes(lanes(_difference(offset, interval)), 2),
|
|
225
|
+
_planes(lanes(rotation_residual), 2),
|
|
226
|
+
_planes(lanes(position_residual), 2),
|
|
227
|
+
_xor(tail, interval).tobytes(),
|
|
228
|
+
]
|
|
229
|
+
data = b''.join(parts)
|
|
230
|
+
assert len(data) == section_bytes(players, count)
|
|
231
|
+
return data
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def decode(
|
|
235
|
+
data: bytes | memoryview,
|
|
236
|
+
players: int,
|
|
237
|
+
count: int,
|
|
238
|
+
interval: int = KEYFRAME_INTERVAL,
|
|
239
|
+
start: int = 0,
|
|
240
|
+
stop: int | None = None,
|
|
241
|
+
) -> np.ndarray:
|
|
242
|
+
"""Returns frames `start` to `stop` of a frames section, as `[frames, frame_bytes]` bytes.
|
|
243
|
+
|
|
244
|
+
Only the frames from the keyframe before `start` decode, so a reader seeks in constant time.
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
data: The frames section, at least `section_bytes(players, count)` bytes.
|
|
248
|
+
players: The number of players.
|
|
249
|
+
count: The number of frames of the section.
|
|
250
|
+
interval: The frames from one keyframe to the next.
|
|
251
|
+
start: The first frame to return.
|
|
252
|
+
stop: The frame after the last to return, by default the end.
|
|
253
|
+
"""
|
|
254
|
+
stop = count if stop is None else max(0, min(stop, count))
|
|
255
|
+
start = max(0, min(start, stop))
|
|
256
|
+
first = start // interval * interval
|
|
257
|
+
span = stop - first
|
|
258
|
+
if start == stop:
|
|
259
|
+
return np.empty((0, frame_bytes(players)), np.uint8)
|
|
260
|
+
buffer = np.frombuffer(data, np.uint8, section_bytes(players, count))
|
|
261
|
+
at = 0
|
|
262
|
+
|
|
263
|
+
def take(lanes: int, width: int = 2) -> np.ndarray:
|
|
264
|
+
nonlocal at
|
|
265
|
+
size = lanes * count * width
|
|
266
|
+
block = buffer[at : at + size].reshape(width, lanes, count)[:, :, first:stop]
|
|
267
|
+
at += size
|
|
268
|
+
return _unplanes(block, width) if width > 1 else block[0]
|
|
269
|
+
|
|
270
|
+
def short(values: np.ndarray) -> np.ndarray:
|
|
271
|
+
# A sum of differences holds the value up to whole turns of 2**16.
|
|
272
|
+
return values.astype(np.int16).astype(np.int64)
|
|
273
|
+
|
|
274
|
+
head = _sum(take(HEAD_WORDS, 4), interval)
|
|
275
|
+
root_position = short(_sum(_sum(take(3 * players), interval), interval))
|
|
276
|
+
root_rotation = short(_sum(take(4 * players), interval))
|
|
277
|
+
turn = short(_sum(take(_LOCAL_ROTATION * players), interval))
|
|
278
|
+
sign = _unxor(take(_SIGNS * players, 1), interval).reshape(_CHILDREN, players, span)
|
|
279
|
+
offsets = short(_sum(take(_LOCAL_OFFSET * players), interval))
|
|
280
|
+
rotation_residual = take(_ROTATION_RESIDUAL * players).reshape(_CHILDREN, 4, players, span)
|
|
281
|
+
position_residual = take(_POSITION_RESIDUAL * players).reshape(_CHILDREN, 3, players, span)
|
|
282
|
+
tail = _unxor(take(3 * padded(players), 1), interval)
|
|
283
|
+
turn = turn.reshape(_CHILDREN, 3, players, span)
|
|
284
|
+
offsets = offsets.reshape(_CHILDREN, 3, players, span)
|
|
285
|
+
|
|
286
|
+
# Components, joints, players and frames, as the encoder holds them.
|
|
287
|
+
nodes = np.empty((7, JOINTS, players, span), np.int64)
|
|
288
|
+
nodes[:3, 0] = root_position.reshape(3, players, span)
|
|
289
|
+
nodes[3:, 0] = root_rotation.reshape(4, players, span)
|
|
290
|
+
for joint in range(1, JOINTS):
|
|
291
|
+
parent = nodes[:, PARENTS[joint]]
|
|
292
|
+
index = joint - 1
|
|
293
|
+
rotation = _predict_rotation(parent[3:], turn[index], sign[index])
|
|
294
|
+
nodes[3:, joint] = short(rotation + rotation_residual[index])
|
|
295
|
+
position = _predict_position(parent[:3], parent[3:], offsets[index])
|
|
296
|
+
nodes[:3, joint] = short(position + position_residual[index])
|
|
297
|
+
out = np.empty((span, frame_bytes(players)), np.uint8)
|
|
298
|
+
out[:, :HEAD_BYTES] = np.ascontiguousarray(head.T).astype('<i4').view(np.uint8)
|
|
299
|
+
nodes_end = HEAD_BYTES + players * JOINTS * 14
|
|
300
|
+
out[:, HEAD_BYTES:nodes_end] = (
|
|
301
|
+
np.ascontiguousarray(nodes.transpose(3, 2, 1, 0))
|
|
302
|
+
.astype('<i2')
|
|
303
|
+
.reshape(span, -1)
|
|
304
|
+
.view(np.uint8)
|
|
305
|
+
)
|
|
306
|
+
out[:, nodes_end:] = tail.T
|
|
307
|
+
return out[start - first :]
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# Copyright 2026 The cuju Authors
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
"""The filters of a recorder, which decide at the end of each episode whether to save it.
|
|
5
|
+
|
|
6
|
+
A filter reads the summary of the episode, its score, goals, length, rewards and annotations, so it
|
|
7
|
+
never touches the batch, and the frames of an episode that it drops are discarded unwritten.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import math
|
|
13
|
+
import struct
|
|
14
|
+
from collections.abc import Callable
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
from cuju.errors import ArgumentError
|
|
18
|
+
|
|
19
|
+
# The names that `keep` takes besides a function.
|
|
20
|
+
KEEP = ('all', 'goals')
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Filter:
|
|
24
|
+
"""Decides from the summary of an ended episode whether a recorder saves it.
|
|
25
|
+
|
|
26
|
+
Every condition must hold, the choice or function of `keep`, every `every`-th episode from the
|
|
27
|
+
first, and a return of at least `min_reward`.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(
|
|
31
|
+
self,
|
|
32
|
+
keep: str | Callable[[dict[str, Any]], Any] | None = None,
|
|
33
|
+
every: int = 1,
|
|
34
|
+
min_reward: float | None = None,
|
|
35
|
+
) -> None:
|
|
36
|
+
"""Checks and keeps the conditions.
|
|
37
|
+
|
|
38
|
+
Raises:
|
|
39
|
+
ArgumentError: If `keep` is neither a function nor one of `KEEP`, `every` is not a
|
|
40
|
+
whole number of at least 1, or `min_reward` is not a finite number.
|
|
41
|
+
"""
|
|
42
|
+
if keep is None:
|
|
43
|
+
keep = 'all'
|
|
44
|
+
if not callable(keep) and keep not in KEEP:
|
|
45
|
+
raise ArgumentError(f'expected keep as a function or one of {KEEP}, found {keep!r}')
|
|
46
|
+
if isinstance(every, bool) or not isinstance(every, int) or every < 1:
|
|
47
|
+
raise ArgumentError(f'expected every as a whole number of at least 1, found {every!r}')
|
|
48
|
+
if min_reward is not None and (
|
|
49
|
+
isinstance(min_reward, bool)
|
|
50
|
+
or not isinstance(min_reward, int | float)
|
|
51
|
+
or not math.isfinite(min_reward)
|
|
52
|
+
):
|
|
53
|
+
raise ArgumentError(f'expected min_reward as a finite number, found {min_reward!r}')
|
|
54
|
+
self.keep = keep
|
|
55
|
+
self.every = every
|
|
56
|
+
self.min_reward = min_reward
|
|
57
|
+
|
|
58
|
+
def __call__(self, summary: dict[str, Any]) -> bool:
|
|
59
|
+
"""Returns whether to save the episode of `summary`, as `episode_summary` returns it."""
|
|
60
|
+
if summary['episode'] % self.every:
|
|
61
|
+
return False
|
|
62
|
+
if self.min_reward is not None and summary['reward'] < self.min_reward:
|
|
63
|
+
return False
|
|
64
|
+
if self.keep == 'goals':
|
|
65
|
+
return summary['goals'] > 0
|
|
66
|
+
if callable(self.keep):
|
|
67
|
+
return bool(self.keep(summary))
|
|
68
|
+
return True
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def step_rewards(batch: Any, environment: int, information: dict[str, Any]) -> list[float]:
|
|
72
|
+
"""Returns the reward of each agent of an environment on a step, none when it is not known.
|
|
73
|
+
|
|
74
|
+
The rewards are those that the batch hands its step observers, read without a change, and the
|
|
75
|
+
agents count in the batch's order, those of the left team first.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
batch: The batch.
|
|
79
|
+
environment: The environment.
|
|
80
|
+
information: What the batch handed its observers for the step.
|
|
81
|
+
"""
|
|
82
|
+
rewards = information.get('reward')
|
|
83
|
+
if rewards is None:
|
|
84
|
+
return []
|
|
85
|
+
reward = rewards[environment].reshape(-1)
|
|
86
|
+
mask = batch.agent_mask[environment].reshape(-1)
|
|
87
|
+
return [
|
|
88
|
+
float(value) for value, kept in zip(reward.tolist(), mask.tolist(), strict=True) if kept
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def episode_summary(
|
|
93
|
+
episode: int,
|
|
94
|
+
frames: list[bytes],
|
|
95
|
+
rewards: list[float],
|
|
96
|
+
environment: int,
|
|
97
|
+
annotations: list[dict[str, Any]] | None = None,
|
|
98
|
+
) -> dict[str, Any]:
|
|
99
|
+
"""Returns the summary of an episode that a filter reads.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
episode: The number of episodes that the recorder ended before this one.
|
|
103
|
+
frames: The frames of the episode.
|
|
104
|
+
rewards: The return of each agent over the captured steps.
|
|
105
|
+
environment: The environment of the recorder.
|
|
106
|
+
annotations: The annotations of the episode, with long keys.
|
|
107
|
+
"""
|
|
108
|
+
last = frames[-1]
|
|
109
|
+
score = list(struct.unpack_from('<2i', last, 8))
|
|
110
|
+
(step,) = struct.unpack_from('<i', last, 0)
|
|
111
|
+
return {
|
|
112
|
+
'episode': episode,
|
|
113
|
+
'environment': environment,
|
|
114
|
+
'score': score,
|
|
115
|
+
'goals': sum(score),
|
|
116
|
+
'length': len(frames),
|
|
117
|
+
'steps': step,
|
|
118
|
+
'rewards': list(rewards),
|
|
119
|
+
'reward': sum(rewards),
|
|
120
|
+
'annotations': list(annotations or ()),
|
|
121
|
+
}
|