rlevo-environments 0.2.0

RL benchmark environments and landscapes for rlevo (internal crate — use `rlevo` for the full API)
//! Physics state for the BipedalWalker environment.
//!
//! [`BipedalWalkerState`] holds all Rapier2D handles required to read body
//! kinematics and drive joints each step, plus cached contact flags and the
//! most recent observation.

use rapier2d::dynamics::{ImpulseJointHandle, RigidBodyHandle};
use rlevo_core::base::State;

use super::observation::BipedalWalkerObservation;

/// Physics state for BipedalWalker.
///
/// Stores rapier2d handles for all bodies and joints, plus cached
/// contact flags and the last computed observation.
#[derive(Debug, Clone)]
pub struct BipedalWalkerState {
    /// Hull (torso) rigid body.
    pub hull_handle: RigidBodyHandle,
    /// Upper leg 1 (thigh).
    pub leg1_upper_handle: RigidBodyHandle,
    /// Lower leg 1 (shin).
    pub leg1_lower_handle: RigidBodyHandle,
    /// Upper leg 2 (thigh).
    pub leg2_upper_handle: RigidBodyHandle,
    /// Lower leg 2 (shin).
    pub leg2_lower_handle: RigidBodyHandle,
    /// Hip 1 revolute joint (hull ↔ upper leg 1).
    pub hip1_joint: ImpulseJointHandle,
    /// Knee 1 revolute joint (upper leg 1 ↔ lower leg 1).
    pub knee1_joint: ImpulseJointHandle,
    /// Hip 2 revolute joint (hull ↔ upper leg 2).
    pub hip2_joint: ImpulseJointHandle,
    /// Knee 2 revolute joint (upper leg 2 ↔ lower leg 2).
    pub knee2_joint: ImpulseJointHandle,
    /// Whether leg 1 is in contact with the ground.
    pub leg1_contact: bool,
    /// Whether leg 2 is in contact with the ground.
    pub leg2_contact: bool,
    /// Cached observation from the last `step()` or `reset()`.
    pub last_obs: BipedalWalkerObservation,
}

impl State<1> for BipedalWalkerState {
    type Observation = BipedalWalkerObservation;

    /// Returns `[24]` — the flat observation dimension of the state.
    fn shape() -> [usize; 1] {
        [24]
    }

    /// Returns `true` when the cached observation is fully finite.
    ///
    /// A non-finite observation indicates a physics simulation divergence;
    /// the environment should be reset when this returns `false`.
    fn is_valid(&self) -> bool {
        self.last_obs.is_finite()
    }

    /// Returns 24 — the total number of scalar elements in the state.
    fn numel(&self) -> usize {
        24
    }

    /// Returns a clone of the most recently computed observation.
    ///
    /// The cached value is updated by `BipedalWalker::step` and
    /// `BipedalWalker::reset` before this is called by the environment loop.
    fn observe(&self) -> BipedalWalkerObservation {
        self.last_obs.clone()
    }
}