diff options
| author | srdusr <[email protected]> | 2025-12-07 21:58:00 +0200 |
|---|---|---|
| committer | srdusr <[email protected]> | 2025-12-07 21:58:00 +0200 |
| commit | 8fc4440150ce84c9cc3f5ec13d50beb58a5b65bc (patch) | |
| tree | 4455ba7bc2f94dc2684629880d38997f516cd95a /crates/server | |
| parent | 5b1ea38522dbf6bf60db5a2270463de0c12d9de3 (diff) | |
| download | typerpunk-8fc4440150ce84c9cc3f5ec13d50beb58a5b65bc.tar.gz typerpunk-8fc4440150ce84c9cc3f5ec13d50beb58a5b65bc.zip | |
Rebuild the generic packs, add shell and sysadmin, fix short race passages
The packs were not proper. An audit found 86 of 253 items (33%) carrying an
attribution that just restated the category - prose *about* a topic with an
invented source, which is the same fault the movies pack had. Six of thirteen
packs were mostly that: technology had 15 items and one distinct attribution.
- science, technology, history, nature and business are now sourced quotes
with real attributions: Feynman, Curie, Hopper, Dijkstra, Lincoln, Carson,
Drucker, Goodhart. 82 items, all attributed to a person or a work.
- general is original factual prose, so it now carries no attribution at all
rather than claiming "General knowledge" as a source. merge_packs no longer
invents one from the pack's filename.
- Four explanatory passages in philosophy lost their "Philosophy" attribution
for the same reason.
- One duplicated passage removed.
Generic attributions: 86/253 before, 0/349 now.
New technical packs
- shell: 24 awk, sed and pipeline drills, each explaining what the line does
- field splitting, associative arrays, !seen[$0]++, process substitution,
xargs -0, strict mode.
- sysadmin: 24 operational one-liners across systemd, disk, processes,
network, permissions, SSH, backup and containers.
- programming grew to 37 and hacking to 30, with git bisect, window
functions, EXPLAIN ANALYZE, certificate transparency and capability audits.
- All 115 technical drills carry an explanation.
Race passage length
- Multiplayer drew from the same pool as single player, so a race could land
on a 22-character quote and be over before anyone had their hands in
position. Races now require 120 characters; the filter is applied when the
pool is loaded, not to the packs, since a short quote is fine to type alone.
For reference, TypeRacer organises by difficulty and language rather than
topic: one default English pool of ~11,900 texts plus per-language universes
and specials (accuracy, repeat, easytexts, anime). Their scale comes from user
submission with moderation, which is still the feature this does not have.
Diffstat (limited to 'crates/server')
| -rw-r--r-- | crates/server/src/main.rs | 39 |
1 files changed, 31 insertions, 8 deletions
diff --git a/crates/server/src/main.rs b/crates/server/src/main.rs index e9133b9..0ce169d 100644 --- a/crates/server/src/main.rs +++ b/crates/server/src/main.rs @@ -36,19 +36,42 @@ struct TextEntry { // falls back to a couple of plain sentences if the file isn't reachable // (e.g. the server binary run from somewhere other than the repo root), // so a room can still start rather than erroring on an empty pool. +/// Shortest passage a multiplayer race will use. +const MIN_RACE_TEXT_CHARS: usize = 120; + +fn fallback_race_texts() -> Vec<RaceText> { + vec![ + RaceText { + text: "The quick brown fox jumps over the lazy dog, and the dog, being lazy, does not mind at all. Pack my box with five dozen liquor jugs.".to_string(), + attribution: None, + }, + ] +} + fn load_race_texts() -> Vec<RaceText> { let path = std::env::var("TEXTS_JSON_PATH").unwrap_or_else(|_| "texts.json".to_string()); match std::fs::read_to_string(&path).ok().and_then(|raw| serde_json::from_str::<Vec<TextEntry>>(&raw).ok()) { - Some(entries) if !entries.is_empty() => entries - .into_iter() - .map(|e| RaceText { text: e.content, attribution: e.attribution }) - .collect(), + Some(entries) if !entries.is_empty() => { + let pool: Vec<RaceText> = entries + .into_iter() + // A race on a 22-character quote is over before anyone has + // their hands in position. The dataset is shared with single + // player, where a short quote is fine, so the floor is applied + // here rather than to the pack itself. + .filter(|e| e.content.chars().count() >= MIN_RACE_TEXT_CHARS) + .map(|e| RaceText { text: e.content, attribution: e.attribution }) + .collect(); + if pool.is_empty() { + tracing::warn!("no passage reached {MIN_RACE_TEXT_CHARS} characters - races will use the fallback pool"); + fallback_race_texts() + } else { + tracing::info!("{} passages available for races", pool.len()); + pool + } + } _ => { tracing::warn!("could not load race texts from {path} - using a small built-in fallback pool"); - vec![ - RaceText { text: "The quick brown fox jumps over the lazy dog.".to_string(), attribution: None }, - RaceText { text: "Pack my box with five dozen liquor jugs.".to_string(), attribution: None }, - ] + fallback_race_texts() } } } |