refactor(api): rename Agent to Competitor and .player field to .rating
Competitor holds dynamic per-history state (message, last_time) for someone competing; its configuration lives in a Rating. AgentStore renamed to CompetitorStore to match. The internal `clean()` free function's parameter name changed from `agents` to `competitors` for consistency. Local variable names (agent_idx, this_agent) inside history.rs are left unchanged — they represent abstract identifiers, not Competitor instances. Part of T2 of docs/superpowers/specs/2026-04-23-trueskill-engine-redesign-design.md.
This commit is contained in:
@@ -1,125 +0,0 @@
|
||||
use crate::{Index, agent::Agent, drift::Drift};
|
||||
|
||||
/// Dense Vec-backed store for agent state in History.
|
||||
///
|
||||
/// Indexed directly by Index.0, eliminating HashMap hashing in the
|
||||
/// forward/backward sweep. Uses `Vec<Option<Agent<D>>>` so slots can be
|
||||
/// absent without an explicit present mask.
|
||||
#[derive(Debug)]
|
||||
pub struct AgentStore<D: Drift> {
|
||||
agents: Vec<Option<Agent<D>>>,
|
||||
n_present: usize,
|
||||
}
|
||||
|
||||
impl<D: Drift> Default for AgentStore<D> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
agents: Vec::new(),
|
||||
n_present: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Drift> AgentStore<D> {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn ensure_capacity(&mut self, idx: usize) {
|
||||
if idx >= self.agents.len() {
|
||||
self.agents.resize_with(idx + 1, || None);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, idx: Index, agent: Agent<D>) {
|
||||
self.ensure_capacity(idx.0);
|
||||
if self.agents[idx.0].is_none() {
|
||||
self.n_present += 1;
|
||||
}
|
||||
self.agents[idx.0] = Some(agent);
|
||||
}
|
||||
|
||||
pub fn get(&self, idx: Index) -> Option<&Agent<D>> {
|
||||
self.agents.get(idx.0).and_then(|slot| slot.as_ref())
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, idx: Index) -> Option<&mut Agent<D>> {
|
||||
self.agents.get_mut(idx.0).and_then(|slot| slot.as_mut())
|
||||
}
|
||||
|
||||
pub fn contains(&self, idx: Index) -> bool {
|
||||
self.get(idx).is_some()
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.n_present
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.n_present == 0
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = (Index, &Agent<D>)> {
|
||||
self.agents
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(i, slot)| slot.as_ref().map(|a| (Index(i), a)))
|
||||
}
|
||||
|
||||
pub fn iter_mut(&mut self) -> impl Iterator<Item = (Index, &mut Agent<D>)> {
|
||||
self.agents
|
||||
.iter_mut()
|
||||
.enumerate()
|
||||
.filter_map(|(i, slot)| slot.as_mut().map(|a| (Index(i), a)))
|
||||
}
|
||||
|
||||
pub fn values_mut(&mut self) -> impl Iterator<Item = &mut Agent<D>> {
|
||||
self.agents.iter_mut().filter_map(|s| s.as_mut())
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Drift> std::ops::Index<Index> for AgentStore<D> {
|
||||
type Output = Agent<D>;
|
||||
fn index(&self, idx: Index) -> &Agent<D> {
|
||||
self.get(idx).expect("agent not found at index")
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Drift> std::ops::IndexMut<Index> for AgentStore<D> {
|
||||
fn index_mut(&mut self, idx: Index) -> &mut Agent<D> {
|
||||
self.get_mut(idx).expect("agent not found at index")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{agent::Agent, drift::ConstantDrift};
|
||||
|
||||
#[test]
|
||||
fn insert_then_get() {
|
||||
let mut store: AgentStore<ConstantDrift> = AgentStore::new();
|
||||
let idx = Index(7);
|
||||
store.insert(idx, Agent::default());
|
||||
assert!(store.contains(idx));
|
||||
assert_eq!(store.len(), 1);
|
||||
assert!(store.get(idx).is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iter_in_index_order() {
|
||||
let mut store: AgentStore<ConstantDrift> = AgentStore::new();
|
||||
store.insert(Index(2), Agent::default());
|
||||
store.insert(Index(0), Agent::default());
|
||||
store.insert(Index(5), Agent::default());
|
||||
let keys: Vec<Index> = store.iter().map(|(i, _)| i).collect();
|
||||
assert_eq!(keys, vec![Index(0), Index(2), Index(5)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn index_operator_works() {
|
||||
let mut store: AgentStore<ConstantDrift> = AgentStore::new();
|
||||
store.insert(Index(3), Agent::default());
|
||||
let _ = &store[Index(3)];
|
||||
}
|
||||
}
|
||||
127
src/storage/competitor_store.rs
Normal file
127
src/storage/competitor_store.rs
Normal file
@@ -0,0 +1,127 @@
|
||||
use crate::{Index, competitor::Competitor, drift::Drift};
|
||||
|
||||
/// Dense Vec-backed store for competitor state in History.
|
||||
///
|
||||
/// Indexed directly by Index.0, eliminating HashMap hashing in the
|
||||
/// forward/backward sweep. Uses `Vec<Option<Competitor<D>>>` so slots can be
|
||||
/// absent without an explicit present mask.
|
||||
#[derive(Debug)]
|
||||
pub struct CompetitorStore<D: Drift> {
|
||||
competitors: Vec<Option<Competitor<D>>>,
|
||||
n_present: usize,
|
||||
}
|
||||
|
||||
impl<D: Drift> Default for CompetitorStore<D> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
competitors: Vec::new(),
|
||||
n_present: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Drift> CompetitorStore<D> {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn ensure_capacity(&mut self, idx: usize) {
|
||||
if idx >= self.competitors.len() {
|
||||
self.competitors.resize_with(idx + 1, || None);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, idx: Index, competitor: Competitor<D>) {
|
||||
self.ensure_capacity(idx.0);
|
||||
if self.competitors[idx.0].is_none() {
|
||||
self.n_present += 1;
|
||||
}
|
||||
self.competitors[idx.0] = Some(competitor);
|
||||
}
|
||||
|
||||
pub fn get(&self, idx: Index) -> Option<&Competitor<D>> {
|
||||
self.competitors.get(idx.0).and_then(|slot| slot.as_ref())
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, idx: Index) -> Option<&mut Competitor<D>> {
|
||||
self.competitors
|
||||
.get_mut(idx.0)
|
||||
.and_then(|slot| slot.as_mut())
|
||||
}
|
||||
|
||||
pub fn contains(&self, idx: Index) -> bool {
|
||||
self.get(idx).is_some()
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.n_present
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.n_present == 0
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = (Index, &Competitor<D>)> {
|
||||
self.competitors
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(i, slot)| slot.as_ref().map(|a| (Index(i), a)))
|
||||
}
|
||||
|
||||
pub fn iter_mut(&mut self) -> impl Iterator<Item = (Index, &mut Competitor<D>)> {
|
||||
self.competitors
|
||||
.iter_mut()
|
||||
.enumerate()
|
||||
.filter_map(|(i, slot)| slot.as_mut().map(|a| (Index(i), a)))
|
||||
}
|
||||
|
||||
pub fn values_mut(&mut self) -> impl Iterator<Item = &mut Competitor<D>> {
|
||||
self.competitors.iter_mut().filter_map(|s| s.as_mut())
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Drift> std::ops::Index<Index> for CompetitorStore<D> {
|
||||
type Output = Competitor<D>;
|
||||
fn index(&self, idx: Index) -> &Competitor<D> {
|
||||
self.get(idx).expect("competitor not found at index")
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Drift> std::ops::IndexMut<Index> for CompetitorStore<D> {
|
||||
fn index_mut(&mut self, idx: Index) -> &mut Competitor<D> {
|
||||
self.get_mut(idx).expect("competitor not found at index")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{competitor::Competitor, drift::ConstantDrift};
|
||||
|
||||
#[test]
|
||||
fn insert_then_get() {
|
||||
let mut store: CompetitorStore<ConstantDrift> = CompetitorStore::new();
|
||||
let idx = Index(7);
|
||||
store.insert(idx, Competitor::default());
|
||||
assert!(store.contains(idx));
|
||||
assert_eq!(store.len(), 1);
|
||||
assert!(store.get(idx).is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iter_in_index_order() {
|
||||
let mut store: CompetitorStore<ConstantDrift> = CompetitorStore::new();
|
||||
store.insert(Index(2), Competitor::default());
|
||||
store.insert(Index(0), Competitor::default());
|
||||
store.insert(Index(5), Competitor::default());
|
||||
let keys: Vec<Index> = store.iter().map(|(i, _)| i).collect();
|
||||
assert_eq!(keys, vec![Index(0), Index(2), Index(5)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn index_operator_works() {
|
||||
let mut store: CompetitorStore<ConstantDrift> = CompetitorStore::new();
|
||||
store.insert(Index(3), Competitor::default());
|
||||
let _ = &store[Index(3)];
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
mod agent_store;
|
||||
mod competitor_store;
|
||||
mod skill_store;
|
||||
|
||||
pub use agent_store::AgentStore;
|
||||
pub use competitor_store::CompetitorStore;
|
||||
pub(crate) use skill_store::SkillStore;
|
||||
|
||||
Reference in New Issue
Block a user