refactor: one word per concept

Three vocabulary collisions, from #75.

**"rating" meant three things**, one the opposite of the exported type.
`Rating` is documented as static *configuration* — "this returns what it
was told", against every other accessor's "what inference inferred". But
`quality`'s parameter was `rating_groups: &[&[Gaussian]]` and its prose
said "rating groups" four times, where "rating" means a *posterior* — the
one thing `Rating` is documented not to be. Two error messages used it
that way too.

So a reader who learned `Rating = config` passed `Rating` values to
`quality`, which takes `Gaussian`; and one who learned "rating = what
comes out" was baffled that `h.rating(&k)` is not their skill.

"rating" is now reserved for the type. `quality(teams: &[&[Gaussian]])`,
and "every rating is finite" became "every posterior is finite".

**"agent" was a private fourth name for a competitor** — ~200 identifiers
against 236 uses of "competitor", and it leaked into two `pub` signatures
on `TimeSlice`. Now that #73 has made those internal this is a pure
rename, so the crate has one word for the entity throughout.

**"player" survived in one public signature** — `free_for_all(players:)`
plus two doc lines. Renamed, along with three internal closure bindings.
Doc examples that use "player" as a *key* are left alone: that is a
user's data, not the crate's vocabulary.

The panic-message expectations in tests/quality.rs moved with the prose,
which is the point of asserting on message text — the tests caught the
rename rather than papering over it.

Not touched: "performance" (always skill widened by beta), "skill",
"member" and "team" are each used for exactly one thing already.

Refs #75

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011hcFjNDmHXZF8URGLku5zZ
This commit is contained in:
2026-09-09 20:54:47 +02:00
co-authored by Claude Opus 5
parent 85c4d0d87d
commit fdd1539cab
6 changed files with 234 additions and 207 deletions
+16 -14
View File
@@ -284,7 +284,9 @@ impl<'a, T: Time, D: Drift<T>> Game<'a, T, D> {
self.teams[t]
.iter()
.zip(self.weights[t].iter())
.fold(N00, |p, (player, &w)| p + (player.performance() * w))
.fold(N00, |p, (competitor, &w)| {
p + (competitor.performance() * w)
})
}));
let n_diffs = n_teams.saturating_sub(1);
@@ -361,18 +363,18 @@ impl<'a, T: Time, D: Drift<T>> Game<'a, T, D> {
.iter()
.zip(self.weights.iter())
.enumerate()
.map(|(orig_i, (players, weights))| {
.map(|(orig_i, (competitors, weights))| {
let si = arena.inv_buf[orig_i];
let m = arena.lhood_win[si] * arena.lhood_lose[si];
// Already folded into `team_prior` at the top of the chain,
// indexed by sorted position.
let performance = arena.team_prior[si];
players
competitors
.iter()
.zip(weights.iter())
.map(|(player, &w)| {
((m - performance.exclude(player.performance() * w)) * (1.0 / w))
.forget(player.beta.powi(2))
.map(|(competitor, &w)| {
((m - performance.exclude(competitor.performance() * w)) * (1.0 / w))
.forget(competitor.beta.powi(2))
})
.collect::<Vec<_>>()
})
@@ -577,7 +579,7 @@ impl<T: Time, D: Drift<T>> Game<'_, T, D> {
))
}
/// Convenience wrapper over [`Game::ranked`] for two single-player teams.
/// Convenience wrapper over [`Game::ranked`] for two single-competitor teams.
///
/// # Errors
///
@@ -597,14 +599,14 @@ impl<T: Time, D: Drift<T>> Game<'_, T, D> {
/// # Errors
///
/// Wraps each player in a one-member team and delegates to
/// Wraps each competitor in a one-member team and delegates to
/// [`Game::ranked`], so it returns the same errors.
pub fn free_for_all(
players: &[&Rating<T, D>],
competitors: &[&Rating<T, D>],
outcome: crate::Outcome,
options: &GameOptions,
) -> Result<OwnedGame<T, D>, crate::InferenceError> {
let teams: Vec<Vec<Rating<T, D>>> = players.iter().map(|p| vec![**p]).collect();
let teams: Vec<Vec<Rating<T, D>>> = competitors.iter().map(|p| vec![**p]).collect();
let team_refs: Vec<&[Rating<T, D>]> = teams.iter().map(|t| t.as_slice()).collect();
Self::ranked(&team_refs, outcome, options)
}
@@ -1428,8 +1430,8 @@ mod tests {
#[test]
fn run_chain_honours_max_iter_in_convergence_options() {
let players: Vec<R> = (0..4).map(|_| R::default()).collect();
let teams: Vec<Vec<_>> = players.iter().map(|p| vec![*p]).collect();
let competitors: Vec<R> = (0..4).map(|_| R::default()).collect();
let teams: Vec<Vec<_>> = competitors.iter().map(|p| vec![*p]).collect();
let result = vec![3.0, 2.0, 1.0, 0.0];
let weights = vec![vec![1.0]; 4];
@@ -1476,8 +1478,8 @@ mod tests {
#[test]
fn run_chain_with_damping_converges_to_same_posterior() {
let players: Vec<R> = (0..4).map(|_| R::default()).collect();
let teams: Vec<Vec<_>> = players.iter().map(|p| vec![*p]).collect();
let competitors: Vec<R> = (0..4).map(|_| R::default()).collect();
let teams: Vec<Vec<_>> = competitors.iter().map(|p| vec![*p]).collect();
let result = vec![3.0, 2.0, 1.0, 0.0];
let weights = vec![vec![1.0]; 4];