diff options
| -rw-r--r-- | Cargo.lock | 59 | ||||
| -rw-r--r-- | crates/cli/Cargo.toml | 1 | ||||
| -rw-r--r-- | crates/cli/src/run.rs | 96 | ||||
| -rw-r--r-- | crates/cli/src/words.rs | 12 | ||||
| -rw-r--r-- | lib/mihi/src/lib.rs | 14 |
5 files changed, 165 insertions, 17 deletions
@@ -48,6 +48,7 @@ version = "0.1.0" dependencies = [ "inquire", "mihi", + "rand", "serde", "serde_json", "tempfile", @@ -316,6 +317,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] name = "proc-macro2" version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -340,6 +350,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom", +] + +[[package]] name = "redox_syscall" version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -734,6 +773,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] +name = "zerocopy" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] name = "zmij" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index cd2a8c9..5cfe588 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -14,3 +14,4 @@ inquire = { version = "0.7.5", features = ["editor"] } serde = { version = "1", features = ["derive"] } serde_json = "1" tempfile = "3.20" +rand = "0.9" diff --git a/crates/cli/src/run.rs b/crates/cli/src/run.rs index 21a92c2..45149ac 100644 --- a/crates/cli/src/run.rs +++ b/crates/cli/src/run.rs @@ -1,9 +1,11 @@ +extern crate rand; use inquire::{Confirm, Editor, Text}; -use mihi::{configuration, get_adjective_table, get_inflected_from}; +use mihi::{configuration, get_adjective_table, get_inflected_from, select_related_words}; use mihi::{ get_noun_table, select_relevant_words, touch_exercise, update_success, Category, - DeclensionTable, Exercise, ExerciseKind, Word, + DeclensionTable, Exercise, ExerciseKind, RelationKind, Word, }; +use rand::prelude::*; use std::env; use std::fs; use std::io::Write; @@ -182,16 +184,102 @@ fn ask_for_table(word: &Word, table: &DeclensionTable, id: Option<&str>) -> bool same_answer(&solution, &expected) } +// Ask for alternative forms (gendered or otherwise) about a given word. +fn ask_for_alternatives(related: &[Vec<Word>; 5]) -> bool { + let alternatives = &related[RelationKind::Alternative as usize - 1]; + if !alternatives.is_empty() { + let Ok(raw) = + Text::new("Do you know of any alternative (not asking about a gendered one)?").prompt() + else { + return false; + }; + let expected = mihi::joint_related_words(alternatives); + if !same_answer(&raw, &expected) { + return false; + } + } + + let gendered = &related[RelationKind::Gendered as usize - 1]; + if !gendered.is_empty() { + let Ok(raw) = Text::new("Do you know of the same word but on the other gender?").prompt() + else { + return false; + }; + let expected = mihi::joint_related_words(gendered); + if !same_answer(&raw, &expected) { + return false; + } + } + + true +} + +// Ask for other forms for the given word (i.e. comparative, superlative, +// adverbial). +// +// NOTE: this word _has_ to be an adjective. +fn ask_for_others(word: &Word, related: &[Vec<Word>; 5]) -> bool { + assert!(matches!(word.category, Category::Adjective)); + + let comparative = mihi::comparative(word, &related[RelationKind::Comparative as usize - 1]); + let Ok(raw) = Text::new("Comparative:").prompt() else { + return false; + }; + if !same_answer(&raw, &comparative) { + return false; + } + + let superlative = mihi::superlative(word, &related[RelationKind::Superlative as usize - 1]); + let Ok(raw) = Text::new("Superlative:").prompt() else { + return false; + }; + if !same_answer(&raw, &superlative) { + return false; + } + + let adverbial = mihi::adverb(word, &related[RelationKind::Adverb as usize - 1]); + let Ok(raw) = Text::new("Adverb:").prompt() else { + return false; + }; + if !same_answer(&raw, &adverbial) { + return false; + } + + true +} + fn good_noun_inflection(word: &Word) -> bool { if let Ok(table) = get_noun_table(word) { - return ask_for_table(word, &table, None); + if !ask_for_table(word, &table, None) { + return false; + } + if let Ok(related) = select_related_words(word) { + return ask_for_alternatives(&related); + } } true } fn good_adjective_inflection(word: &Word) -> bool { if let Ok(tables) = get_adjective_table(word) { - ask_for_table(word, &tables[0], Some("in the masculine")); + // Pick which gender from the adjective table to ask. + let mut rng = rand::rng(); + let gender = rng.random_range(0..=2); + let suffix = match gender { + 1 => Some("in the feminine"), + 2 => Some("in the neuter"), + _ => Some("in the masculine"), + }; + + if !ask_for_table(word, &tables[gender], suffix) { + return false; + } + if let Ok(related) = select_related_words(word) { + if !ask_for_others(word, &related) { + return false; + } + return ask_for_alternatives(&related); + } } true } diff --git a/crates/cli/src/words.rs b/crates/cli/src/words.rs index 181ea81..413d193 100644 --- a/crates/cli/src/words.rs +++ b/crates/cli/src/words.rs @@ -653,8 +653,8 @@ fn show_info(word: Word) -> Result<(), String> { let alternatives = &related[RelationKind::Alternative as usize - 1]; match alternatives.len() { 0 => {} - 1 => println!("Alternative: {}", joint_related_words(&alternatives)), - _ => println!("Alternatives: {}", joint_related_words(&alternatives)), + 1 => println!("Alternative: {}", joint_related_words(alternatives)), + _ => println!("Alternatives: {}", joint_related_words(alternatives)), } let gendered = &related[RelationKind::Gendered as usize - 1]; let g = if matches!(word.gender, Gender::Masculine) { @@ -664,8 +664,8 @@ fn show_info(word: Word) -> Result<(), String> { }; match gendered.len() { 0 => {} - 1 => println!("{g} alternative: {}", joint_related_words(&gendered)), - _ => println!("{g} alternatives: {}", joint_related_words(&gendered)), + 1 => println!("{g} alternative: {}", joint_related_words(gendered)), + _ => println!("{g} alternatives: {}", joint_related_words(gendered)), } // Show translation if available. @@ -826,8 +826,8 @@ mod tests { format!( "{}-{}-{}", first, - mihi::joint_related_words(&alternatives), - mihi::joint_related_words(&gendered) + mihi::joint_related_words(alternatives), + mihi::joint_related_words(gendered) ) } diff --git a/lib/mihi/src/lib.rs b/lib/mihi/src/lib.rs index 7555a0f..d75965d 100644 --- a/lib/mihi/src/lib.rs +++ b/lib/mihi/src/lib.rs @@ -300,18 +300,18 @@ impl TryFrom<isize> for RelationKind { } /// Join by enunciate the given words. -pub fn joint_related_words(related: &Vec<Word>) -> String { - return related +pub fn joint_related_words(related: &[Word]) -> String { + related .iter() .map(|w| w.enunciated.clone()) .collect::<Vec<String>>() - .join("; "); + .join("; ") } /// Returns a string with the enunciate of the comparative form of the given /// `word`. This function assumes that it really does, or at least it's /// contained in the `related` vector. -pub fn comparative(word: &Word, related: &Vec<Word>) -> String { +pub fn comparative(word: &Word, related: &[Word]) -> String { if !related.is_empty() { return joint_related_words(related); } @@ -326,7 +326,7 @@ pub fn comparative(word: &Word, related: &Vec<Word>) -> String { /// Returns a string with the enunciate of the superlative form of the given /// `word`. This function assumes that it really does, or at least it's /// contained in the `related` vector. -pub fn superlative(word: &Word, related: &Vec<Word>) -> String { +pub fn superlative(word: &Word, related: &[Word]) -> String { if !related.is_empty() { return joint_related_words(related); } @@ -346,7 +346,7 @@ pub fn superlative(word: &Word, related: &Vec<Word>) -> String { /// Returns a string with the enunciate of the adverbial form of the given /// `word`. This function assumes that it really does, or at least it's /// contained in the `related` vector. -pub fn adverb(word: &Word, related: &Vec<Word>) -> String { +pub fn adverb(word: &Word, related: &[Word]) -> String { if !related.is_empty() { return joint_related_words(related); } @@ -439,7 +439,7 @@ impl Word { if self.is_flag_set("contracted_root") { return format!( "{}{}", - self.particle[0..(self.particle.len() - 2)].to_string(), + &self.particle[0..(self.particle.len() - 2)], self.particle.chars().last().unwrap_or(' '), ); } |
