From 3ab839e543b8a4c64c8b1ca24e1750a9f13382d6 Mon Sep 17 00:00:00 2001 From: Landon Taylor Date: Fri, 18 Apr 2025 13:18:48 -0600 Subject: [PATCH] Final Touches --- README.md | 9 +-- src/course/story.rs | 8 +- src/course/tracker.rs | 12 +-- src/course/units.rs | 150 ++++++++++++++++++------------------ src/learner/profile.rs | 6 +- src/utilities/files.rs | 2 +- src/utilities/generators.rs | 48 ++++++++---- src/utilities/printers.rs | 116 +++++++++++++--------------- src/utilities/questions.rs | 16 ++-- 9 files changed, 188 insertions(+), 179 deletions(-) diff --git a/README.md b/README.md index faf2c61..22a3edd 100644 --- a/README.md +++ b/README.md @@ -50,10 +50,9 @@ Contributions are welcome! Please follow the steps at https://gitmoss.fyi/GitMos ## License -This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. +This project is licensed under the MIT License. See the [LICENSE](LICENSE.md) file for details. -## Acknowledgments +## Notes -- Inspired by the need for critical thinking in AI education. -- Special thanks to the AI Applications in Education course team at Utah State University. -- Open-source contributors and the developer community. \ No newline at end of file +- Special thanks to Seth Poulsen and his AI Applications in Education course at Utah State University. +- Portions of this work are produced by generative AI, but all content is manually reviewed and edited. \ No newline at end of file diff --git a/src/course/story.rs b/src/course/story.rs index 09b521a..c3b7104 100644 --- a/src/course/story.rs +++ b/src/course/story.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -use crate::{files, learner::profile, printers::{self, clear_console, print_boxed, print_screen}, utilities::questions::{ask_mcq, ask_multi_select, ask_plaintext}, Module}; +use crate::{files, learner::profile, printers::{self, clear_console, print_boxed, print_screen}, utilities::{generators::random_affirmation, questions::{ask_mcq, ask_multi_select, ask_plaintext}}, Module}; use super::units; const REQUIRED_FOR_MASTERY: f64 = 0.1; @@ -97,7 +97,7 @@ pub fn inner_loop(unit: Module, learner: &mut profile::Learner) { print_screen(&screen.text); } ScreenType::Mcq => { - let options: Vec<&str> = screen.options.as_ref().unwrap().iter().map(|s| s.as_str()).collect(); + // let options: Vec<&str> = screen.options.as_ref().unwrap().iter().map(|s| s.as_str()).collect(); let mut options = screen.options.clone().unwrap(); options.push("Get hint".to_string()); let options: Vec<&str> = options.iter().map(|s| s.as_str()).collect(); @@ -129,7 +129,7 @@ pub fn inner_loop(unit: Module, learner: &mut profile::Learner) { } } ScreenType::Checkboxes => { - let options: Vec<&str> = screen.options.as_ref().unwrap().iter().map(|s| s.as_str()).collect(); + // let options: Vec<&str> = screen.options.as_ref().unwrap().iter().map(|s| s.as_str()).collect(); let mut options = screen.options.clone().unwrap(); options.push("Get hint".to_string()); let options: Vec<&str> = options.iter().map(|s| s.as_str()).collect(); @@ -139,7 +139,7 @@ pub fn inner_loop(unit: Module, learner: &mut profile::Learner) { let selected_indices: Vec = answers.iter().filter_map(|answer| options.iter().position(|x| *x == *answer)).collect(); let correct = selected_indices.iter().all(|&index| correct_indices.contains(&index)) && selected_indices.len() == correct_indices.len(); if correct { - print_boxed("Correct!", printers::StatementType::CorrectFeedback); + print_boxed(&format!("Correct! {}", random_affirmation()), printers::StatementType::CorrectFeedback); learner.update_progress(unit.clone(), true); learner.progress.get_mut(&unit).unwrap().print_progress(); break; diff --git a/src/course/tracker.rs b/src/course/tracker.rs index ff070a5..032ba9a 100644 --- a/src/course/tracker.rs +++ b/src/course/tracker.rs @@ -90,12 +90,12 @@ impl Tracker { self.milestones_completed as f64 / self.milestones as f64 } - pub fn get_milestones(&self) -> usize { - self.milestones - } - pub fn get_milestones_completed(&self) -> usize { - self.milestones_completed - } + // pub fn get_milestones(&self) -> usize { + // self.milestones + // } + // pub fn get_milestones_completed(&self) -> usize { + // self.milestones_completed + // } pub(crate) fn print_progress(&self) { let (cols, _) = size().unwrap(); diff --git a/src/course/units.rs b/src/course/units.rs index 9c48173..d8c5251 100644 --- a/src/course/units.rs +++ b/src/course/units.rs @@ -1,8 +1,6 @@ use serde::{Deserialize, Serialize}; use std::fmt; -use rand::{rng, seq::IndexedRandom}; -use std::fs; -use std::path::Path; +// use rand::{rng, seq::IndexedRandom}; // use strum_macros::EnumIter; /// Module defines the structure and data for the course units, including lessons and modules. @@ -40,16 +38,16 @@ impl Module { Module::Appraisal(appraisal) => format!("data/appraisal/{}.json", appraisal.get_filename()), } } - pub(crate) fn random() -> Self { - let mut rng = rng(); - let variants = [ - Module::Logic(Logic::random()), - Module::Fallacy(Fallacy::random()), - Module::Bias(Bias::random()), - Module::Appraisal(Appraisal::random()), - ]; - variants.choose(&mut rng).unwrap().clone() - } + // pub(crate) fn random() -> Self { + // let mut rng = rng(); + // let variants = [ + // Module::Logic(Logic::random()), + // Module::Fallacy(Fallacy::random()), + // Module::Bias(Bias::random()), + // Module::Appraisal(Appraisal::random()), + // ]; + // variants.choose(&mut rng).unwrap().clone() + // } pub fn iter() -> impl Iterator { vec![ Module::Introduction, @@ -125,11 +123,11 @@ impl Logic { Logic::BooleanAlgebra => "boolean_algebra".to_string(), } } - pub(crate) fn random() -> Self { - let mut rng = rng(); - let variants = [Logic::LogicalOperations, Logic::BooleanAlgebra]; - variants.choose(&mut rng).unwrap().clone() - } + // pub(crate) fn random() -> Self { + // let mut rng = rng(); + // let variants = [Logic::LogicalOperations, Logic::BooleanAlgebra]; + // variants.choose(&mut rng).unwrap().clone() + // } } /// Fallacy module, which includes various lessons related to logical fallacies and errors in reasoning. @@ -158,14 +156,14 @@ impl Fallacy { Fallacy::InformalFallacy(informal) => format!("informal/{}", informal.get_filename()), } } - pub(crate) fn random() -> Self { - let mut rng = rng(); - let variants = [ - Fallacy::FormalFallacy(FormalFallacy::random()), - Fallacy::InformalFallacy(InformalFallacy::random()), - ]; - variants.choose(&mut rng).unwrap().clone() - } + // pub(crate) fn random() -> Self { + // let mut rng = rng(); + // let variants = [ + // Fallacy::FormalFallacy(FormalFallacy::random()), + // Fallacy::InformalFallacy(InformalFallacy::random()), + // ]; + // variants.choose(&mut rng).unwrap().clone() + // } } /// Formal fallacies, which are errors in the structure of an argument. #[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Hash)] @@ -199,16 +197,16 @@ impl FormalFallacy { FormalFallacy::QuantificationalFallacy => "quantificational".to_string(), } } - pub(crate) fn random() -> Self { - let mut rng = rng(); - let variants = [ - FormalFallacy::PropositionalFallacy, - FormalFallacy::ProbabilisticFallacy, - FormalFallacy::SyllogisticFallacy, - FormalFallacy::QuantificationalFallacy, - ]; - variants.choose(&mut rng).unwrap().clone() - } + // pub(crate) fn random() -> Self { + // let mut rng = rng(); + // let variants = [ + // FormalFallacy::PropositionalFallacy, + // FormalFallacy::ProbabilisticFallacy, + // FormalFallacy::SyllogisticFallacy, + // FormalFallacy::QuantificationalFallacy, + // ]; + // variants.choose(&mut rng).unwrap().clone() + // } } /// Informal fallacies, which are errors in reasoning that do not involve the structure of the argument. #[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Hash)] @@ -272,26 +270,26 @@ impl InformalFallacy { InformalFallacy::BeggingTheQuestion => "begging_the_question".to_string(), } } - pub(crate) fn random() -> Self { - let mut rng = rng(); - let variants = [ - InformalFallacy::PostHocErgoPropterHoc, - InformalFallacy::SlipperySlope, - InformalFallacy::TexasSharpshooter, - InformalFallacy::HastyGeneralization, - InformalFallacy::OverGeneralization, - InformalFallacy::NoTrueScotsman, - InformalFallacy::QuotingOutOfContext, - InformalFallacy::AdHominem, - InformalFallacy::TuQuoque, - InformalFallacy::Bandwagon, - InformalFallacy::StrawMan, - InformalFallacy::AdIgnorantiam, - InformalFallacy::SpecialPleading, - InformalFallacy::BeggingTheQuestion, - ]; - variants.choose(&mut rng).unwrap().clone() - } + // pub(crate) fn random() -> Self { + // let mut rng = rng(); + // let variants = [ + // InformalFallacy::PostHocErgoPropterHoc, + // InformalFallacy::SlipperySlope, + // InformalFallacy::TexasSharpshooter, + // InformalFallacy::HastyGeneralization, + // InformalFallacy::OverGeneralization, + // InformalFallacy::NoTrueScotsman, + // InformalFallacy::QuotingOutOfContext, + // InformalFallacy::AdHominem, + // InformalFallacy::TuQuoque, + // InformalFallacy::Bandwagon, + // InformalFallacy::StrawMan, + // InformalFallacy::AdIgnorantiam, + // InformalFallacy::SpecialPleading, + // InformalFallacy::BeggingTheQuestion, + // ]; + // variants.choose(&mut rng).unwrap().clone() + // } } /// Bias module, which includes various lessons related to cognitive biases and their impact on reasoning. @@ -332,18 +330,18 @@ impl Bias { Bias::BarnumEffect => "barnum_effect".to_string(), } } - pub(crate) fn random() -> Self { - let mut rng = rng(); - let variants = [ - Bias::ConfirmationBias, - Bias::TheHaloEffect, - Bias::FundamentalAttributionError, - Bias::InGroupBias, - Bias::DunningKrugerEffect, - Bias::BarnumEffect, - ]; - variants.choose(&mut rng).unwrap().clone() - } + // pub(crate) fn random() -> Self { + // let mut rng = rng(); + // let variants = [ + // Bias::ConfirmationBias, + // Bias::TheHaloEffect, + // Bias::FundamentalAttributionError, + // Bias::InGroupBias, + // Bias::DunningKrugerEffect, + // Bias::BarnumEffect, + // ]; + // variants.choose(&mut rng).unwrap().clone() + // } } /// Appraisal module, which includes various lessons related to the evaluation and appraisal of arguments. #[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Hash)] @@ -371,12 +369,12 @@ impl Appraisal { Appraisal::CounterArgument => "counter_argument".to_string(), } } - pub(crate) fn random() -> Self { - let mut rng = rng(); - let variants = [ - Appraisal::ConversionToPropositional, - Appraisal::CounterArgument, - ]; - variants.choose(&mut rng).unwrap().clone() - } + // pub(crate) fn random() -> Self { + // let mut rng = rng(); + // let variants = [ + // Appraisal::ConversionToPropositional, + // Appraisal::CounterArgument, + // ]; + // variants.choose(&mut rng).unwrap().clone() + // } } diff --git a/src/learner/profile.rs b/src/learner/profile.rs index 898a8b6..0401217 100644 --- a/src/learner/profile.rs +++ b/src/learner/profile.rs @@ -60,16 +60,16 @@ impl Learner { clear_console(); let (cols, _) = size().unwrap(); - let dashes = "-".repeat((cols as usize)); + let dashes = "-".repeat(cols as usize); let dots = " ".repeat((cols as usize).saturating_sub(42)); println!("Progress Report for {}:", self.name); println!("{}", dashes); println!("{}", format!(" Unit {} | Mastery |", dots).bold()); - let mut modules = units::Module::iter(); + let modules = units::Module::iter(); for module in modules { - if let Some(progress) = self.progress.get(&module) { + if let Some(_) = self.progress.get(&module) { self.progress.get(&module).unwrap().print_progress(); } } diff --git a/src/utilities/files.rs b/src/utilities/files.rs index f155045..927c0f9 100644 --- a/src/utilities/files.rs +++ b/src/utilities/files.rs @@ -2,7 +2,7 @@ use std::{fs::File, io::Read}; use serde::de::DeserializeOwned; -use crate::{course::story, learner::profile::{self, Learner}}; +use crate::{course::story, learner::profile::Learner}; // pub fn save_learner_progress(learner: &Learner, file: &str) -> Result<(), std::io::Error> { // learner.clone().save_to_file(file)?; diff --git a/src/utilities/generators.rs b/src/utilities/generators.rs index 2e39671..481ae06 100644 --- a/src/utilities/generators.rs +++ b/src/utilities/generators.rs @@ -69,7 +69,7 @@ const OFFENSIVE_TERMS: [&str; 20] = [ "jew", "slut", "tit", "phag" ]; -const affirmations: [&str; 8] = [ +const AFFIRMATIONS: [&str; 26] = [ "You're doing great!", "Keep up the good work!", "Fantastic effort!", @@ -78,12 +78,30 @@ const affirmations: [&str; 8] = [ "You're making progress!", "Keep it up!", "Great work!", + "You're unstoppable!", + "Amazing progress!", + "You're crushing it!", + "Keep shining!", + "You're a star!", + "Outstanding work!", + "You're making a difference!", + "You're an inspiration!", + "You're reaching for the stars!", + "Keep shining like a supernova!", + "You're out of this world!", + "You're orbiting success!", + "You're a cosmic force!", + "You're a shooting star!", + "You're exploring new galaxies!", + "You're a stellar achiever!", + "You're blazing a trail through the cosmos!", + "You're a beacon in the universe!", ]; pub fn random_affirmation() -> String { let mut rng = rand::rng(); - let index = rng.random_range(0..affirmations.len()); - affirmations[index].to_string() + let index = rng.random_range(0..AFFIRMATIONS.len()); + AFFIRMATIONS[index].to_string() } pub fn generate_name() -> String { @@ -109,15 +127,15 @@ pub fn generate_name() -> String { } -pub fn random_unit_intro() -> String { - let mut rng = rand::rng(); - let unit_intros: [&str; 5] = [ - "It's time to learn about", - "Let's dive into", - "Get ready to explore", - "Prepare to discover", - "Let's embark on a journey to learn about", - ]; - let index = rng.random_range(0..unit_intros.len()); - unit_intros[index].to_string() -} \ No newline at end of file +// pub fn random_unit_intro() -> String { +// let mut rng = rand::rng(); +// let unit_intros: [&str; 5] = [ +// "It's time to learn about", +// "Let's dive into", +// "Get ready to explore", +// "Prepare to discover", +// "Let's embark on a journey to learn about", +// ]; +// let index = rng.random_range(0..unit_intros.len()); +// unit_intros[index].to_string() +// } \ No newline at end of file diff --git a/src/utilities/printers.rs b/src/utilities/printers.rs index 4464a90..e0437f0 100644 --- a/src/utilities/printers.rs +++ b/src/utilities/printers.rs @@ -1,14 +1,12 @@ use crossterm::{execute, style::Stylize, terminal::{size, Clear, ClearType}}; use std::io::stdout; -use super::generators; - -const DELAY_SECONDS: u64 = 1; +// const DELAY_SECONDS: u64 = 1; #[derive(PartialEq)] pub enum StatementType { Default, - DelayExplanation, + // DelayExplanation, Question, GeneralFeedback, CorrectFeedback, @@ -160,10 +158,6 @@ pub fn print_boxed(instruction: &str, statement_type: StatementType) { StatementType::Question => { return; } - StatementType::DelayExplanation => { - wait_for_input_delay(DELAY_SECONDS); - print_boxed(&instruction, StatementType::Default); - } _ => { wait_for_input(); } @@ -171,29 +165,29 @@ pub fn print_boxed(instruction: &str, statement_type: StatementType) { } /// Prints a full-width ruler of designated characters -pub fn print_rule(character: char, padding: (bool, bool)) { - let (cols, _) = size().unwrap(); +// pub fn print_rule(character: char, padding: (bool, bool)) { +// let (cols, _) = size().unwrap(); - println!( - "{}{}{}", - if padding.0 { "\n" } else { "" }, - character.to_string().repeat(cols as usize), - if padding.1 { "\n" } else { "" } - ); -} +// println!( +// "{}{}{}", +// if padding.0 { "\n" } else { "" }, +// character.to_string().repeat(cols as usize), +// if padding.1 { "\n" } else { "" } +// ); +// } /// Horizontally center the desired text on the screen -pub fn print_centered(text: &str) { - let (cols, _) = size().unwrap(); - let text_width = text.len() as u16; - let start_col = (cols.saturating_sub(text_width)) / 2; +// pub fn print_centered(text: &str) { +// let (cols, _) = size().unwrap(); +// let text_width = text.len() as u16; +// let start_col = (cols.saturating_sub(text_width)) / 2; - println!( - "\x1B[{}C{}", - start_col, - text - ); -} +// println!( +// "\x1B[{}C{}", +// start_col, +// text +// ); +// } /// Wait for input from the user pub fn wait_for_input() { @@ -213,51 +207,51 @@ pub fn wait_for_input() { } /// Wait for input from the user -pub fn print_title(title: &str) { - let (cols, _) = size().unwrap(); - let text_width = title.len() as u16; - let start_col = (cols.saturating_sub(text_width)) / 2 + 1; +// pub fn print_title(title: &str) { +// let (cols, _) = size().unwrap(); +// let text_width = title.len() as u16; +// let start_col = (cols.saturating_sub(text_width)) / 2 + 1; - println!( - "\x1B[{}C{}", - start_col, - title.bold() - ); +// println!( +// "\x1B[{}C{}", +// start_col, +// title.bold() +// ); - wait_for_input(); -} +// wait_for_input(); +// } /// Wait for input from the user -pub fn wait_for_input_delay(seconds: u64) { - let prompt = format!("Remember to be mindful. Advance in {} seconds.", seconds); - let (cols, _) = size().unwrap(); - let text_width = prompt.len() as u16; - let start_col = (cols.saturating_sub(text_width)) / 2 + 1; +// pub fn wait_for_input_delay(seconds: u64) { +// let prompt = format!("Remember to be mindful. Advance in {} seconds.", seconds); +// let (cols, _) = size().unwrap(); +// let text_width = prompt.len() as u16; +// let start_col = (cols.saturating_sub(text_width)) / 2 + 1; - println!( - "\x1B[{}C{}", - start_col, - prompt.italic().dark_blue() - ); +// println!( +// "\x1B[{}C{}", +// start_col, +// prompt.italic().dark_blue() +// ); - std::thread::sleep(std::time::Duration::from_secs(DELAY_SECONDS)); +// std::thread::sleep(std::time::Duration::from_secs(DELAY_SECONDS)); -} +// } /// Wait for input from the user -pub fn inform_delay(seconds: u64) { - let prompt = format!("Remember to be mindful. Answers appear in {} seconds.", seconds); - let (cols, _) = size().unwrap(); - let text_width = prompt.len() as u16; - let start_col = (cols.saturating_sub(text_width)) / 2 + 1; +// pub fn inform_delay(seconds: u64) { +// let prompt = format!("Remember to be mindful. Answers appear in {} seconds.", seconds); +// let (cols, _) = size().unwrap(); +// let text_width = prompt.len() as u16; +// let start_col = (cols.saturating_sub(text_width)) / 2 + 1; - println!( - "\x1B[{}C{}", - start_col, - prompt.italic().dark_blue() - ); +// println!( +// "\x1B[{}C{}", +// start_col, +// prompt.italic().dark_blue() +// ); -} +// } pub fn unit_screen(title: &str) { clear_console(); diff --git a/src/utilities/questions.rs b/src/utilities/questions.rs index e94b497..6d63bec 100644 --- a/src/utilities/questions.rs +++ b/src/utilities/questions.rs @@ -1,16 +1,16 @@ use inquire::{MultiSelect, Select, Text}; use crossterm::terminal; -use super::printers::{clear_console, inform_delay, print_boxed, StatementType}; +use super::printers::{clear_console, print_boxed, StatementType}; -const DELAY_SECONDS: u64 = 1; +// const DELAY_SECONDS: u64 = 1; -pub fn ask_mcq_delayed(question: &str, options: &[&str]) -> Option { - print_boxed(question, StatementType::Question); - inform_delay(DELAY_SECONDS); - std::thread::sleep(std::time::Duration::from_secs(DELAY_SECONDS)); - ask_mcq(question, options) -} +// pub fn ask_mcq_delayed(question: &str, options: &[&str]) -> Option { +// print_boxed(question, StatementType::Question); +// inform_delay(DELAY_SECONDS); +// std::thread::sleep(std::time::Duration::from_secs(DELAY_SECONDS)); +// ask_mcq(question, options) +// } /// Print a quiz question in an attractive format and get the user's choice pub fn ask_mcq(question: &str, options: &[&str]) -> Option {