use crate::env::games::bucket_brigade::NUM_HOUSES;
#[allow(dead_code)]
const HOUSE_RUINED: u8 = 2;
const HOUSE_BURNING: u8 = 1;
#[allow(dead_code)]
const HOUSE_SAFE: u8 = 0;
const MODE_REST: i64 = 0;
const MODE_WORK: i64 = 1;
pub fn specialist_action(
flat_obs: &[f32],
agent_id: usize,
num_agents: usize,
num_houses: usize,
) -> [i64; 3] {
assert!(
flat_obs.len() > num_houses,
"flat_obs (len {}) too short for num_houses = {} (need at least {} entries)",
flat_obs.len(),
num_houses,
1 + num_houses,
);
assert!(
agent_id < num_agents,
"agent_id {agent_id} out of range for num_agents {num_agents}"
);
let mut first_owned: Option<usize> = None;
let mut first_burning_owned: Option<usize> = None;
for h in 0..num_houses {
if h % num_agents == agent_id {
if first_owned.is_none() {
first_owned = Some(h);
}
let state = flat_obs[1 + h] as u8;
if state == HOUSE_BURNING && first_burning_owned.is_none() {
first_burning_owned = Some(h);
}
}
}
if let Some(h) = first_burning_owned {
return [h as i64, MODE_WORK, MODE_WORK];
}
if let Some(h) = first_owned {
return [h as i64, MODE_REST, MODE_REST];
}
[0, MODE_REST, MODE_REST]
}
pub fn specialist_action_canonical(
flat_obs: &[f32],
agent_id: usize,
num_agents: usize,
) -> [i64; 3] {
specialist_action(flat_obs, agent_id, num_agents, NUM_HOUSES)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BucketBrigadeCell {
Beta01,
Beta05,
Beta09,
}
impl BucketBrigadeCell {
pub fn parameters(&self) -> (f32, f32, f32) {
match self {
BucketBrigadeCell::Beta01 => (0.1, 0.1, 0.5),
BucketBrigadeCell::Beta05 => (0.5, 0.1, 0.5),
BucketBrigadeCell::Beta09 => (0.9, 0.1, 0.5),
}
}
pub fn tag(&self) -> &'static str {
match self {
BucketBrigadeCell::Beta01 => "b0.10_k0.10_c0.50",
BucketBrigadeCell::Beta05 => "b0.50_k0.10_c0.50",
BucketBrigadeCell::Beta09 => "b0.90_k0.10_c0.50",
}
}
pub fn all() -> [BucketBrigadeCell; 3] {
[BucketBrigadeCell::Beta01, BucketBrigadeCell::Beta05, BucketBrigadeCell::Beta09]
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_obs(houses: &[u8], num_houses: usize) -> Vec<f32> {
let mut obs = vec![0.0; 1 + num_houses + 64];
for (i, &h) in houses.iter().enumerate().take(num_houses) {
obs[1 + i] = h as f32;
}
obs
}
#[test]
fn all_safe_rests_on_lowest_owned() {
let obs = make_obs(&[0; NUM_HOUSES], NUM_HOUSES);
assert_eq!(specialist_action(&obs, 0, 4, NUM_HOUSES), [0, MODE_REST, MODE_REST]);
assert_eq!(specialist_action(&obs, 1, 4, NUM_HOUSES), [1, MODE_REST, MODE_REST]);
assert_eq!(specialist_action(&obs, 2, 4, NUM_HOUSES), [2, MODE_REST, MODE_REST]);
assert_eq!(specialist_action(&obs, 3, 4, NUM_HOUSES), [3, MODE_REST, MODE_REST]);
}
#[test]
fn single_burning_owned_works_with_honest_signal() {
let mut houses = [0u8; NUM_HOUSES];
houses[4] = HOUSE_BURNING;
let obs = make_obs(&houses, NUM_HOUSES);
assert_eq!(specialist_action(&obs, 0, 4, NUM_HOUSES), [4, MODE_WORK, MODE_WORK]);
assert_eq!(specialist_action(&obs, 1, 4, NUM_HOUSES), [1, MODE_REST, MODE_REST]);
assert_eq!(specialist_action(&obs, 2, 4, NUM_HOUSES), [2, MODE_REST, MODE_REST]);
assert_eq!(specialist_action(&obs, 3, 4, NUM_HOUSES), [3, MODE_REST, MODE_REST]);
}
#[test]
fn multiple_burning_owned_picks_lowest() {
let mut houses = [0u8; NUM_HOUSES];
houses[5] = HOUSE_BURNING;
houses[9] = HOUSE_BURNING;
let obs = make_obs(&houses, NUM_HOUSES);
assert_eq!(specialist_action(&obs, 1, 4, NUM_HOUSES), [5, MODE_WORK, MODE_WORK]);
}
#[test]
fn burning_not_owned_is_ignored() {
let mut houses = [0u8; NUM_HOUSES];
houses[0] = HOUSE_BURNING;
let obs = make_obs(&houses, NUM_HOUSES);
assert_eq!(specialist_action(&obs, 1, 4, NUM_HOUSES), [1, MODE_REST, MODE_REST]);
}
#[test]
fn ruined_house_is_ignored() {
let mut houses = [0u8; NUM_HOUSES];
houses[4] = HOUSE_RUINED;
let obs = make_obs(&houses, NUM_HOUSES);
assert_eq!(specialist_action(&obs, 0, 4, NUM_HOUSES), [0, MODE_REST, MODE_REST]);
}
#[test]
fn matches_python_handpicked_observation() {
let mut houses = [0u8; NUM_HOUSES];
houses[1] = HOUSE_BURNING;
houses[4] = HOUSE_BURNING;
let obs = make_obs(&houses, NUM_HOUSES);
assert_eq!(specialist_action(&obs, 0, 4, NUM_HOUSES), [4, MODE_WORK, MODE_WORK]);
assert_eq!(specialist_action(&obs, 1, 4, NUM_HOUSES), [1, MODE_WORK, MODE_WORK]);
assert_eq!(specialist_action(&obs, 2, 4, NUM_HOUSES), [2, MODE_REST, MODE_REST]);
assert_eq!(specialist_action(&obs, 3, 4, NUM_HOUSES), [3, MODE_REST, MODE_REST]);
}
#[test]
fn cell_parameters() {
assert_eq!(BucketBrigadeCell::Beta01.parameters(), (0.1, 0.1, 0.5));
assert_eq!(BucketBrigadeCell::Beta05.parameters(), (0.5, 0.1, 0.5));
assert_eq!(BucketBrigadeCell::Beta09.parameters(), (0.9, 0.1, 0.5));
}
#[test]
fn cell_tags() {
assert_eq!(BucketBrigadeCell::Beta01.tag(), "b0.10_k0.10_c0.50");
assert_eq!(BucketBrigadeCell::Beta05.tag(), "b0.50_k0.10_c0.50");
assert_eq!(BucketBrigadeCell::Beta09.tag(), "b0.90_k0.10_c0.50");
}
}