use rand::{Rng, SeedableRng};
use crate::env::{Environment, SpaceInfo, SpaceType, StepInfo, StepResult};
#[derive(Debug, Clone)]
pub struct SimpleBanditState {
pub state: f32,
pub steps: usize,
}
#[derive(Debug)]
pub struct SimpleBandit {
state: f32,
steps: usize,
max_steps: usize,
rng: rand::rngs::StdRng,
}
impl SimpleBandit {
pub fn new() -> Self {
Self { state: 0.0, steps: 0, max_steps: 100, rng: rand::rngs::StdRng::from_os_rng() }
}
}
impl Default for SimpleBandit {
fn default() -> Self {
Self::new()
}
}
impl Environment for SimpleBandit {
type Action = i64;
type State = SimpleBanditState;
fn reset(&mut self) {
self.state = self.rng.random_range(0..2) as f32;
self.steps = 0;
}
fn get_observation(&self) -> Vec<f32> {
vec![self.state]
}
fn step(&mut self, action: i64) -> StepResult {
let reward = if action == self.state as i64 {
1.0
} else {
0.0
};
self.steps += 1;
let terminated = self.steps >= self.max_steps;
self.state = self.rng.random_range(0..2) as f32;
StepResult {
observation: self.get_observation(),
reward,
terminated,
truncated: false,
info: StepInfo::default(),
}
}
fn observation_space(&self) -> SpaceInfo {
SpaceInfo {
shape: vec![1], space_type: SpaceType::Box,
}
}
fn action_space(&self) -> SpaceInfo {
SpaceInfo {
shape: vec![],
space_type: SpaceType::Discrete(2), }
}
fn render(&self) -> Vec<u8> {
Vec::new()
}
fn close(&mut self) {
}
fn clone_state(&self) -> SimpleBanditState {
SimpleBanditState { state: self.state, steps: self.steps }
}
fn restore_state(&mut self, state: &SimpleBanditState) {
self.state = state.state;
self.steps = state.steps;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_simple_bandit_rewards() {
let mut env = SimpleBandit::new();
env.reset();
let initial_state = env.state as i64;
let result = env.step(initial_state);
assert_eq!(result.reward, 1.0);
env.state = 0.0;
let result = env.step(1);
assert_eq!(result.reward, 0.0);
env.state = 1.0;
let result = env.step(0);
assert_eq!(result.reward, 0.0);
}
#[test]
fn clone_restore_round_trips() {
let mut env = SimpleBandit::new();
env.reset();
for _ in 0..5 {
env.step(0);
}
let snap = env.clone_state();
let pre_state = env.state;
let pre_steps = env.steps;
let r1 = env.step(pre_state as i64);
env.restore_state(&snap);
assert_eq!(env.state, pre_state, "state must round-trip");
assert_eq!(env.steps, pre_steps, "steps must round-trip");
let r2 = env.step(pre_state as i64);
assert_eq!(r1.reward, r2.reward);
assert_eq!(r1.terminated, r2.terminated);
}
#[test]
fn test_episode_length() {
let mut env = SimpleBandit::new();
env.reset();
for i in 0..99 {
let result = env.step(0);
assert!(!result.terminated, "Episode terminated early at step {}", i);
}
let result = env.step(0);
assert!(result.terminated, "Episode should terminate at step 100");
}
}