Migrate to Gitea
This commit is contained in:
		
							
								
								
									
										3
									
								
								src/course/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/course/mod.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
pub(crate) mod units;
 | 
			
		||||
pub(crate) mod story;
 | 
			
		||||
pub(crate) mod tracker;
 | 
			
		||||
							
								
								
									
										218
									
								
								src/course/story.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										218
									
								
								src/course/story.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,218 @@
 | 
			
		||||
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 super::units;
 | 
			
		||||
 | 
			
		||||
const REQUIRED_FOR_MASTERY: f64 = 0.1;
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize, Deserialize, Debug)]
 | 
			
		||||
pub struct UnitStory {
 | 
			
		||||
    unit: Module,
 | 
			
		||||
    title: String,
 | 
			
		||||
    screens: Vec<Screen>
 | 
			
		||||
}
 | 
			
		||||
#[derive(Serialize, Deserialize, Debug)]
 | 
			
		||||
enum ScreenType {
 | 
			
		||||
    Text,
 | 
			
		||||
    Mcq,
 | 
			
		||||
    Checkboxes,
 | 
			
		||||
    FreeResponse,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize, Deserialize, Debug)]
 | 
			
		||||
pub struct Screen {
 | 
			
		||||
    screen_type: ScreenType,
 | 
			
		||||
    text: String,
 | 
			
		||||
    question: Option<String>,
 | 
			
		||||
    options: Option<Vec<String>>,
 | 
			
		||||
    correct_indices: Option<Vec<usize>>,
 | 
			
		||||
    correct_text: Option<Vec<String>>,
 | 
			
		||||
    hints: Option<Vec<String>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn outer_loop(learner: &mut profile::Learner) {
 | 
			
		||||
    loop {
 | 
			
		||||
        let next_unit = select_next_unit(&learner);
 | 
			
		||||
        if next_unit.is_none() {
 | 
			
		||||
            print_boxed("Congratulations! You have completed all the modules. You are now a master of the galaxy! I have nothing new to teach you. Well done.", printers::StatementType::CorrectFeedback);
 | 
			
		||||
            println!("Saving your progress...");
 | 
			
		||||
            files::save_to_file(&learner.filename, &learner)
 | 
			
		||||
                .expect("Failed to save profile file");
 | 
			
		||||
            println!("Progress saved. Goodbye!");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        let next_unit = next_unit.unwrap();
 | 
			
		||||
        inner_loop(next_unit.clone(), learner);
 | 
			
		||||
        if learner.progress.get(&next_unit).map_or(true, |p| p.get_probability() >= 1.0) {
 | 
			
		||||
            print_boxed(&format!("You have completed {:?}", next_unit), printers::StatementType::GeneralFeedback);
 | 
			
		||||
        } else {
 | 
			
		||||
            print_boxed(&format!("I think we still need to practice {:?}. Let's keep practicing before we move on.", next_unit), printers::StatementType::GeneralFeedback);
 | 
			
		||||
        }
 | 
			
		||||
        let answer = ask_mcq("Do you want to keep going or leave and save?", &[
 | 
			
		||||
            "Keep working (and save the galaxy)",
 | 
			
		||||
            "Leave and save (risking all life in the galaxy)",
 | 
			
		||||
        ]).unwrap();
 | 
			
		||||
        if answer == "Keep working (and save the galaxy)" {
 | 
			
		||||
            print_boxed(&format!("Great! Let's carry on."), printers::StatementType::GeneralFeedback);
 | 
			
		||||
            println!("Saving your progress...");
 | 
			
		||||
            files::save_to_file(&learner.filename, &learner)
 | 
			
		||||
                .expect("Failed to save profile file");
 | 
			
		||||
            println!("Progress saved. Let's keep going!");
 | 
			
		||||
            learner.display_progress();
 | 
			
		||||
            continue;
 | 
			
		||||
        } else {
 | 
			
		||||
            println!("Bummer. We were really counting on you, but I guess you probably have human needs to attend to.");
 | 
			
		||||
            println!("Saving your progress...");
 | 
			
		||||
            files::save_to_file(&learner.filename, &learner)
 | 
			
		||||
                .expect("Failed to save profile file");
 | 
			
		||||
            println!("Progress saved. Goodbye!");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn inner_loop(unit: Module, learner: &mut profile::Learner) {
 | 
			
		||||
    print_boxed(&format!("The unit is {}", unit), printers::StatementType::GeneralFeedback);
 | 
			
		||||
    let filename = unit.get_filename();
 | 
			
		||||
    let unit_story = files::parse_unit(&filename).unwrap();
 | 
			
		||||
    let mut milestones = 0;
 | 
			
		||||
    printers::unit_screen(&unit_story.title);
 | 
			
		||||
    for screen in &unit_story.screens {
 | 
			
		||||
        match screen.screen_type {
 | 
			
		||||
            ScreenType::Text => { }
 | 
			
		||||
            _ => {
 | 
			
		||||
                milestones += 1;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if let Some(progress) = learner.progress.get_mut(&unit) {
 | 
			
		||||
        progress.set_milestones(milestones);
 | 
			
		||||
        progress.set_completed_milestones(0);
 | 
			
		||||
    }
 | 
			
		||||
    for screen in unit_story.screens {
 | 
			
		||||
        let mut attempts = 0;
 | 
			
		||||
        match screen.screen_type {
 | 
			
		||||
            ScreenType::Text => {
 | 
			
		||||
                clear_console();
 | 
			
		||||
                print_screen(&screen.text);
 | 
			
		||||
            }
 | 
			
		||||
            ScreenType::Mcq => {
 | 
			
		||||
                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();
 | 
			
		||||
                loop {
 | 
			
		||||
                    let answer = ask_mcq(&screen.question.clone().unwrap(), &options).unwrap();
 | 
			
		||||
                    let correct = if screen.correct_indices.as_ref().unwrap().contains(&options.iter().position(|x| *x == answer).unwrap_or(usize::MAX)) {
 | 
			
		||||
                        print_boxed("Correct!", printers::StatementType::CorrectFeedback);
 | 
			
		||||
                        true
 | 
			
		||||
                    } else {
 | 
			
		||||
                        if screen.hints.is_some() {
 | 
			
		||||
                            if attempts == screen.hints.iter().len() {
 | 
			
		||||
                                let correct_indices_text: Vec<_> = screen.correct_indices.unwrap().iter().map(|&i| options[i]).collect();
 | 
			
		||||
                                print_boxed(&format!("It seems like these hints aren't working. The answer is: {:?}", correct_indices_text), printers::StatementType::IncorrectFeedback);
 | 
			
		||||
                                break;
 | 
			
		||||
                            } else {
 | 
			
		||||
                                let hint = screen.hints.as_ref().unwrap()[attempts].clone();
 | 
			
		||||
                                print_boxed(&format!("Please try the question again. Here's a hint: {}", hint), printers::StatementType::IncorrectFeedback);
 | 
			
		||||
                                println!("Please try the question again. Here's a hint: {}", hint);
 | 
			
		||||
                                attempts += 1;
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        false
 | 
			
		||||
                    };
 | 
			
		||||
                    learner.update_progress(unit.clone(), correct);
 | 
			
		||||
                    learner.progress.get_mut(&unit).unwrap().print_progress();
 | 
			
		||||
                    if correct {
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            ScreenType::Checkboxes => {
 | 
			
		||||
                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();
 | 
			
		||||
                loop {
 | 
			
		||||
                    let answers = ask_multi_select(&screen.question.clone().unwrap(), &options).unwrap();
 | 
			
		||||
                    let correct_indices: Vec<usize> = screen.correct_indices.as_ref().unwrap().to_vec();
 | 
			
		||||
                    let selected_indices: Vec<usize> = 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);
 | 
			
		||||
                        learner.update_progress(unit.clone(), true);
 | 
			
		||||
                        learner.progress.get_mut(&unit).unwrap().print_progress();
 | 
			
		||||
                        break;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        if screen.hints.is_some() {
 | 
			
		||||
                            if attempts == screen.hints.iter().len() {
 | 
			
		||||
                                print_boxed(&format!("It seems like these hints aren't working. The correct answers are: {:?}", correct_indices.iter().map(|&i| options[i]).collect::<Vec<_>>()), printers::StatementType::IncorrectFeedback);
 | 
			
		||||
                                break;
 | 
			
		||||
                            } else {
 | 
			
		||||
                                let hint = screen.hints.as_ref().unwrap()[attempts].clone();
 | 
			
		||||
                                let hint = format!("{}\nThere are {} correct answers.", hint, correct_indices.len());
 | 
			
		||||
                                print_boxed(&format!("Please try the question again. Here's a hint: {}", hint), printers::StatementType::IncorrectFeedback);
 | 
			
		||||
                                println!("Please try the question again. Here's a hint: {}", hint);
 | 
			
		||||
                                attempts += 1;
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        learner.update_progress(unit.clone(), false);
 | 
			
		||||
                        learner.progress.get_mut(&unit).unwrap().print_progress();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            ScreenType::FreeResponse => {
 | 
			
		||||
                loop {
 | 
			
		||||
                    let answer= ask_plaintext(&screen.question.clone().unwrap()).unwrap();
 | 
			
		||||
                    let correct_text: Vec<String> = screen.correct_text.as_ref().unwrap().to_vec();
 | 
			
		||||
                    let normalized_answer = answer.trim().to_lowercase().replace(|c: char| !c.is_alphanumeric(), "");
 | 
			
		||||
                    let correct = screen.correct_text.as_ref().unwrap().iter().any(|i| {
 | 
			
		||||
                        let normalized_correct_answer = i.trim().to_lowercase().replace(|c: char| !c.is_alphanumeric(), "");
 | 
			
		||||
                        normalized_answer == normalized_correct_answer
 | 
			
		||||
                    });
 | 
			
		||||
                    if correct {
 | 
			
		||||
                        print_boxed("Correct!", printers::StatementType::CorrectFeedback);
 | 
			
		||||
                        learner.update_progress(unit.clone(), true);
 | 
			
		||||
                        learner.progress.get_mut(&unit).unwrap().print_progress();
 | 
			
		||||
                        break;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        if screen.hints.is_some() {
 | 
			
		||||
                            if attempts == screen.hints.iter().len() {
 | 
			
		||||
                                print_boxed(&format!("It seems like these hints aren't working. The correct answers are: {:?}", correct_text), printers::StatementType::IncorrectFeedback);
 | 
			
		||||
                                break;
 | 
			
		||||
                            } else {
 | 
			
		||||
                                let hint = screen.hints.as_ref().unwrap()[attempts].clone();
 | 
			
		||||
                                print_boxed(&format!("Please try the question again. Here's a hint: {}", hint), printers::StatementType::IncorrectFeedback);
 | 
			
		||||
                                println!("Please try the question again. Here's a hint: {}", hint);
 | 
			
		||||
                                attempts += 1;
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        learner.update_progress(unit.clone(), false);
 | 
			
		||||
                        learner.progress.get_mut(&unit).unwrap().print_progress();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn select_next_unit(learner: &profile::Learner) -> Option<units::Module> {
 | 
			
		||||
    let mut modules = units::Module::iter();
 | 
			
		||||
    if learner.progress.get(&Module::Introduction).map_or(true, |p| p.get_probability() == 0.0) {
 | 
			
		||||
        return Some(crate::Module::Introduction);
 | 
			
		||||
    }
 | 
			
		||||
    for module in modules {
 | 
			
		||||
        if let Some(progress) = learner.progress.get(&module) {
 | 
			
		||||
            if progress.get_probability() < REQUIRED_FOR_MASTERY {
 | 
			
		||||
                if progress.get_probability() != 0.0 {
 | 
			
		||||
                    print_boxed(&format!("It looks like you haven't mastered {} yet. Let's work on that.", module), printers::StatementType::GeneralFeedback);
 | 
			
		||||
                }
 | 
			
		||||
                return Some(module);
 | 
			
		||||
            }
 | 
			
		||||
            // if progress.get_milestones_completed() == 0 {
 | 
			
		||||
            //     return Some(module);
 | 
			
		||||
            // }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return None;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										141
									
								
								src/course/tracker.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								src/course/tracker.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,141 @@
 | 
			
		||||
use crossterm::{style::Stylize, terminal::size};
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
use super::units::Module;
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
 | 
			
		||||
pub(crate) struct Tracker {
 | 
			
		||||
    pub(crate) unit: Module,
 | 
			
		||||
    questions_answered: u64,
 | 
			
		||||
    questions_correct: u64,
 | 
			
		||||
    probability_known: f64,
 | 
			
		||||
    results: Vec<bool>, // Vector to store correct/incorrect results
 | 
			
		||||
    milestones: usize,
 | 
			
		||||
    milestones_completed: usize,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Tracker {
 | 
			
		||||
    pub(crate) fn new(unit: Module) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            unit,
 | 
			
		||||
            questions_answered: 0,
 | 
			
		||||
            questions_correct: 0,
 | 
			
		||||
            probability_known: 0.3, // Start with a 30% chance of knowing the answer
 | 
			
		||||
            results: Vec::new(),
 | 
			
		||||
            milestones: 0,
 | 
			
		||||
            milestones_completed: 0,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) fn quiz_result(&mut self, correct: bool) -> f64 {
 | 
			
		||||
        self.questions_answered += 1;
 | 
			
		||||
        self.results.push(correct);
 | 
			
		||||
        if correct {
 | 
			
		||||
            self.milestones_completed = (self.milestones_completed + 1).min(self.milestones);
 | 
			
		||||
            self.questions_correct += 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Calculate streak of correct answers
 | 
			
		||||
        let mut streak = 0;
 | 
			
		||||
        for &result in self.results.iter().rev() {
 | 
			
		||||
            if result {
 | 
			
		||||
                streak += 1;
 | 
			
		||||
            } else {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let prior = self.probability_known;
 | 
			
		||||
        let slip = if self.questions_answered < 10 {
 | 
			
		||||
            0.05 // Lower slip rate for beginners
 | 
			
		||||
        } else if self.probability_known > 0.8 {
 | 
			
		||||
            0.15 // Higher slip rate for advanced students
 | 
			
		||||
        } else {
 | 
			
		||||
            0.1 // Default slip rate
 | 
			
		||||
        };
 | 
			
		||||
        let guess = if self.questions_answered < 10 {
 | 
			
		||||
            0.2 // Lower guess rate for beginners
 | 
			
		||||
        } else if self.probability_known > 0.8 {
 | 
			
		||||
            0.3 // Higher guess rate for advanced students
 | 
			
		||||
        } else {
 | 
			
		||||
            0.25 // Default guess rate (average of 4 options)
 | 
			
		||||
        };
 | 
			
		||||
        let likelihood = if correct {
 | 
			
		||||
            1.0 - slip
 | 
			
		||||
        } else {
 | 
			
		||||
            guess
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // Adjust posterior based on streak
 | 
			
		||||
        let streak_multiplier = 1.0 + (streak as f64 * 0.1); // Increase probability for streaks
 | 
			
		||||
        let posterior = if prior == 0.0 {
 | 
			
		||||
            likelihood * streak_multiplier // Start with likelihood if prior is zero
 | 
			
		||||
        } else {
 | 
			
		||||
            ((likelihood * prior) / ((likelihood * prior) + ((1.0 - likelihood) * (1.0 - prior)))) * streak_multiplier
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        self.probability_known = posterior.min(1.0).max(0.0);
 | 
			
		||||
 | 
			
		||||
        self.probability_known
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) fn get_probability(&self) -> f64 {
 | 
			
		||||
        // self.probability_known
 | 
			
		||||
        if self.milestones == 0 {
 | 
			
		||||
            return 0.0;
 | 
			
		||||
        }
 | 
			
		||||
        if self.milestones_completed == 0 {
 | 
			
		||||
            return 0.0;
 | 
			
		||||
        }
 | 
			
		||||
        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(crate) fn print_progress(&self) {
 | 
			
		||||
        let (cols, _) = size().unwrap();
 | 
			
		||||
        // let percent = self.probability_known * 100.0; // Assuming `probability_known` is a method in `LessonTracker`
 | 
			
		||||
        let percent = if self.milestones_completed > 0 {
 | 
			
		||||
            (self.milestones_completed as f64) / (self.milestones as f64) * 100.0 // Assuming `probability_known` is a method in `LessonTracker`
 | 
			
		||||
        } else {0.0};
 | 
			
		||||
        let percent_name = format!("{:>4.0}%", percent).dark_blue();
 | 
			
		||||
        let unit_name = format!("{} {} ", percent_name, self.unit);
 | 
			
		||||
        let dots = ".".repeat((cols as usize).saturating_sub(unit_name.len() + 11));
 | 
			
		||||
        match percent {
 | 
			
		||||
            percent if percent > 0.0 => {
 | 
			
		||||
                let progress = percent as usize;
 | 
			
		||||
                let filled_percent = progress / 5; // Each '=' represents 5%
 | 
			
		||||
                let bars = format!(
 | 
			
		||||
                    "|{}{}|",
 | 
			
		||||
                    "█".repeat(filled_percent),
 | 
			
		||||
                    " ".repeat(20_usize.saturating_sub(filled_percent)),
 | 
			
		||||
                );
 | 
			
		||||
                println!(
 | 
			
		||||
                    "{} {} {}",
 | 
			
		||||
                    unit_name,
 | 
			
		||||
                    dots,
 | 
			
		||||
                    bars,
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
            _ => {
 | 
			
		||||
                println!(
 | 
			
		||||
                    "{} {} |{}|",
 | 
			
		||||
                    unit_name,
 | 
			
		||||
                    dots,
 | 
			
		||||
                    " ".repeat(20),
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn set_milestones(&mut self, milestones: usize) {
 | 
			
		||||
        self.milestones = milestones;
 | 
			
		||||
    }
 | 
			
		||||
    pub fn set_completed_milestones(&mut self, completed_milestones: usize) {
 | 
			
		||||
        self.milestones_completed = completed_milestones;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										382
									
								
								src/course/units.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										382
									
								
								src/course/units.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,382 @@
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use std::fmt;
 | 
			
		||||
use rand::{rng, seq::IndexedRandom};
 | 
			
		||||
use std::fs;
 | 
			
		||||
use std::path::Path;
 | 
			
		||||
// use strum_macros::EnumIter;
 | 
			
		||||
 | 
			
		||||
/// Module defines the structure and data for the course units, including lessons and modules.
 | 
			
		||||
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Hash, Clone)]
 | 
			
		||||
pub(crate) enum Module {
 | 
			
		||||
    Introduction,
 | 
			
		||||
    Logic(Logic),
 | 
			
		||||
    Fallacy(Fallacy),
 | 
			
		||||
    Bias(Bias),
 | 
			
		||||
    Appraisal(Appraisal),
 | 
			
		||||
}
 | 
			
		||||
impl Default for Module {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Module::Introduction
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
impl fmt::Display for Module {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        match self {
 | 
			
		||||
            Module::Introduction => write!(f, "Introduction"),
 | 
			
		||||
            Module::Logic(logic) => write!(f, "Logic: {}", logic),
 | 
			
		||||
            Module::Fallacy(fallacy) => write!(f, "Fallacy: {}", fallacy),
 | 
			
		||||
            Module::Bias(bias) => write!(f, "Bias: {}", bias),
 | 
			
		||||
            Module::Appraisal(appraisal) => write!(f, "Appraisal: {}", appraisal),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
impl Module {
 | 
			
		||||
    pub(crate) fn get_filename(&self) -> String {
 | 
			
		||||
        match self {
 | 
			
		||||
            Module::Introduction => format!("data/introduction.json"),
 | 
			
		||||
            Module::Logic(logic) => format!("data/logic/{}.json", logic.get_filename()),
 | 
			
		||||
            Module::Fallacy(fallacy) => format!("data/fallacy/{}.json", fallacy.get_filename()),
 | 
			
		||||
            Module::Bias(bias) => format!("data/bias/{}.json", bias.get_filename()),
 | 
			
		||||
            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 fn iter() -> impl Iterator<Item = Self> {
 | 
			
		||||
        vec![
 | 
			
		||||
            Module::Introduction,
 | 
			
		||||
            Module::Logic(Logic::LogicalOperations),
 | 
			
		||||
            Module::Logic(Logic::BooleanAlgebra),
 | 
			
		||||
            Module::Fallacy(Fallacy::FormalFallacy(FormalFallacy::PropositionalFallacy)),
 | 
			
		||||
            Module::Fallacy(Fallacy::FormalFallacy(FormalFallacy::ProbabilisticFallacy)),
 | 
			
		||||
            Module::Fallacy(Fallacy::FormalFallacy(FormalFallacy::SyllogisticFallacy)),
 | 
			
		||||
            Module::Fallacy(Fallacy::FormalFallacy(FormalFallacy::QuantificationalFallacy)),
 | 
			
		||||
            Module::Fallacy(Fallacy::InformalFallacy(InformalFallacy::PostHocErgoPropterHoc)),
 | 
			
		||||
            Module::Fallacy(Fallacy::InformalFallacy(InformalFallacy::SlipperySlope)),
 | 
			
		||||
            Module::Fallacy(Fallacy::InformalFallacy(InformalFallacy::TexasSharpshooter)),
 | 
			
		||||
            Module::Fallacy(Fallacy::InformalFallacy(InformalFallacy::HastyGeneralization)),
 | 
			
		||||
            Module::Fallacy(Fallacy::InformalFallacy(InformalFallacy::OverGeneralization)),
 | 
			
		||||
            Module::Fallacy(Fallacy::InformalFallacy(InformalFallacy::NoTrueScotsman)),
 | 
			
		||||
            Module::Fallacy(Fallacy::InformalFallacy(InformalFallacy::QuotingOutOfContext)),
 | 
			
		||||
            Module::Fallacy(Fallacy::InformalFallacy(InformalFallacy::AdHominem)),
 | 
			
		||||
            Module::Fallacy(Fallacy::InformalFallacy(InformalFallacy::TuQuoque)),
 | 
			
		||||
            Module::Fallacy(Fallacy::InformalFallacy(InformalFallacy::Bandwagon)),
 | 
			
		||||
            Module::Fallacy(Fallacy::InformalFallacy(InformalFallacy::StrawMan)),
 | 
			
		||||
            Module::Fallacy(Fallacy::InformalFallacy(InformalFallacy::AdIgnorantiam)),
 | 
			
		||||
            Module::Fallacy(Fallacy::InformalFallacy(InformalFallacy::SpecialPleading)),
 | 
			
		||||
            Module::Fallacy(Fallacy::InformalFallacy(InformalFallacy::BeggingTheQuestion)),
 | 
			
		||||
            Module::Bias(Bias::ConfirmationBias),
 | 
			
		||||
            Module::Bias(Bias::TheHaloEffect),
 | 
			
		||||
            Module::Bias(Bias::FundamentalAttributionError),
 | 
			
		||||
            Module::Bias(Bias::InGroupBias),
 | 
			
		||||
            Module::Bias(Bias::DunningKrugerEffect),
 | 
			
		||||
            Module::Bias(Bias::BarnumEffect),
 | 
			
		||||
            Module::Appraisal(Appraisal::ConversionToPropositional),
 | 
			
		||||
            Module::Appraisal(Appraisal::CounterArgument),
 | 
			
		||||
        ]
 | 
			
		||||
        .into_iter()
 | 
			
		||||
    }
 | 
			
		||||
    // pub(crate) fn create_empty_files() -> std::io::Result<()> {
 | 
			
		||||
 | 
			
		||||
    //     for module in Module::iter() {
 | 
			
		||||
    //         let filename = module.get_filename();
 | 
			
		||||
    //         let path = Path::new(&filename);
 | 
			
		||||
    //         if let Some(parent) = path.parent() {
 | 
			
		||||
    //             fs::create_dir_all(parent)?;
 | 
			
		||||
    //         }
 | 
			
		||||
    //         fs::File::create(path)?;
 | 
			
		||||
    //     }
 | 
			
		||||
    //     Ok(())
 | 
			
		||||
    // }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// Logic module, which includes various lessons related to logical reasoning and operations.
 | 
			
		||||
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Hash)]
 | 
			
		||||
pub(crate) enum Logic {
 | 
			
		||||
    LogicalOperations,
 | 
			
		||||
    BooleanAlgebra,
 | 
			
		||||
}
 | 
			
		||||
impl Default for Logic {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Logic::LogicalOperations
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
impl std::fmt::Display for Logic {
 | 
			
		||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
			
		||||
        match self {
 | 
			
		||||
            Logic::LogicalOperations => write!(f, "Logical Operations"),
 | 
			
		||||
            Logic::BooleanAlgebra => write!(f, "Boolean Algebra"),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
impl Logic {
 | 
			
		||||
    pub(crate) fn get_filename(&self) -> String {
 | 
			
		||||
        match self {
 | 
			
		||||
            Logic::LogicalOperations => "logical_operations".to_string(),
 | 
			
		||||
            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()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Fallacy module, which includes various lessons related to logical fallacies and errors in reasoning.
 | 
			
		||||
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Hash)]
 | 
			
		||||
pub(crate) enum Fallacy {
 | 
			
		||||
    FormalFallacy(FormalFallacy),
 | 
			
		||||
    InformalFallacy(InformalFallacy),
 | 
			
		||||
}
 | 
			
		||||
impl Default for Fallacy {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Fallacy::FormalFallacy(FormalFallacy::PropositionalFallacy)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
impl std::fmt::Display for Fallacy {
 | 
			
		||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
			
		||||
        match self {
 | 
			
		||||
            Fallacy::FormalFallacy(formal) => write!(f, "Formal: {}", formal),
 | 
			
		||||
            Fallacy::InformalFallacy(informal) => write!(f, "Informal: {}", informal),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
impl Fallacy {
 | 
			
		||||
    pub(crate) fn get_filename(&self) -> String {
 | 
			
		||||
        match self {
 | 
			
		||||
            Fallacy::FormalFallacy(formal) => format!("formal/{}", formal.get_filename()),
 | 
			
		||||
            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()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
/// Formal fallacies, which are errors in the structure of an argument.
 | 
			
		||||
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Hash)]
 | 
			
		||||
pub(crate) enum FormalFallacy {
 | 
			
		||||
    PropositionalFallacy,
 | 
			
		||||
    ProbabilisticFallacy,
 | 
			
		||||
    SyllogisticFallacy,
 | 
			
		||||
    QuantificationalFallacy
 | 
			
		||||
}
 | 
			
		||||
impl Default for FormalFallacy {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        FormalFallacy::PropositionalFallacy
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
impl std::fmt::Display for FormalFallacy {
 | 
			
		||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
			
		||||
        match self {
 | 
			
		||||
            FormalFallacy::PropositionalFallacy => write!(f, "Propositional Fallacy"),
 | 
			
		||||
            FormalFallacy::ProbabilisticFallacy => write!(f, "Probabilistic Fallacy"),
 | 
			
		||||
            FormalFallacy::SyllogisticFallacy => write!(f, "Syllogistic Fallacy"),
 | 
			
		||||
            FormalFallacy::QuantificationalFallacy => write!(f, "Quantificational Fallacy"),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
impl FormalFallacy {
 | 
			
		||||
    pub(crate) fn get_filename(&self) -> String {
 | 
			
		||||
        match self {
 | 
			
		||||
            FormalFallacy::PropositionalFallacy => "propositional".to_string(),
 | 
			
		||||
            FormalFallacy::ProbabilisticFallacy => "probabilistic".to_string(),
 | 
			
		||||
            FormalFallacy::SyllogisticFallacy => "syllogistic".to_string(),
 | 
			
		||||
            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()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
/// Informal fallacies, which are errors in reasoning that do not involve the structure of the argument.
 | 
			
		||||
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Hash)]
 | 
			
		||||
pub(crate) enum InformalFallacy {
 | 
			
		||||
    PostHocErgoPropterHoc,
 | 
			
		||||
    SlipperySlope,
 | 
			
		||||
    TexasSharpshooter,
 | 
			
		||||
    HastyGeneralization,
 | 
			
		||||
    OverGeneralization,
 | 
			
		||||
    NoTrueScotsman,
 | 
			
		||||
    QuotingOutOfContext,
 | 
			
		||||
    AdHominem,
 | 
			
		||||
    TuQuoque,
 | 
			
		||||
    Bandwagon,
 | 
			
		||||
    StrawMan,
 | 
			
		||||
    AdIgnorantiam,
 | 
			
		||||
    SpecialPleading,
 | 
			
		||||
    BeggingTheQuestion
 | 
			
		||||
}
 | 
			
		||||
impl Default for InformalFallacy {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        InformalFallacy::PostHocErgoPropterHoc
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
impl std::fmt::Display for InformalFallacy {
 | 
			
		||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
			
		||||
        match self {
 | 
			
		||||
            InformalFallacy::PostHocErgoPropterHoc => write!(f, "Post Hoc Ergo Propter Hoc"),
 | 
			
		||||
            InformalFallacy::SlipperySlope => write!(f, "Slippery Slope"),
 | 
			
		||||
            InformalFallacy::TexasSharpshooter => write!(f, "Texas Sharpshooter"),
 | 
			
		||||
            InformalFallacy::HastyGeneralization => write!(f, "Hasty Generalization"),
 | 
			
		||||
            InformalFallacy::OverGeneralization => write!(f, "Over Generalization"),
 | 
			
		||||
            InformalFallacy::NoTrueScotsman => write!(f, "No True Scotsman"),
 | 
			
		||||
            InformalFallacy::QuotingOutOfContext => write!(f, "Quoting Out Of Context"),
 | 
			
		||||
            InformalFallacy::AdHominem => write!(f, "Ad Hominem"),
 | 
			
		||||
            InformalFallacy::TuQuoque => write!(f, "Tu Quoque"),
 | 
			
		||||
            InformalFallacy::Bandwagon => write!(f, "Bandwagon"),
 | 
			
		||||
            InformalFallacy::StrawMan => write!(f, "Straw Man"),
 | 
			
		||||
            InformalFallacy::AdIgnorantiam => write!(f, "Ad Ignorantiam"),
 | 
			
		||||
            InformalFallacy::SpecialPleading => write!(f, "Special Pleading"),
 | 
			
		||||
            InformalFallacy::BeggingTheQuestion => write!(f, "Begging The Question"),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
impl InformalFallacy {
 | 
			
		||||
    pub(crate) fn get_filename(&self) -> String {
 | 
			
		||||
        match self {
 | 
			
		||||
            InformalFallacy::PostHocErgoPropterHoc => "post_hoc_ergo_propter_hoc".to_string(),
 | 
			
		||||
            InformalFallacy::SlipperySlope => "slippery_slope".to_string(),
 | 
			
		||||
            InformalFallacy::TexasSharpshooter => "texas_sharpshooter".to_string(),
 | 
			
		||||
            InformalFallacy::HastyGeneralization => "hasty_generalization".to_string(),
 | 
			
		||||
            InformalFallacy::OverGeneralization => "over_generalization".to_string(),
 | 
			
		||||
            InformalFallacy::NoTrueScotsman => "no_true_scotsman".to_string(),
 | 
			
		||||
            InformalFallacy::QuotingOutOfContext => "quoting_out_of_context".to_string(),
 | 
			
		||||
            InformalFallacy::AdHominem => "ad_hominem".to_string(),
 | 
			
		||||
            InformalFallacy::TuQuoque => "tu_quoque".to_string(),
 | 
			
		||||
            InformalFallacy::Bandwagon => "bandwagon".to_string(),
 | 
			
		||||
            InformalFallacy::StrawMan => "straw_man".to_string(),
 | 
			
		||||
            InformalFallacy::AdIgnorantiam => "ad_ignorantiam".to_string(),
 | 
			
		||||
            InformalFallacy::SpecialPleading => "special_pleading".to_string(),
 | 
			
		||||
            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()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Bias module, which includes various lessons related to cognitive biases and their impact on reasoning.
 | 
			
		||||
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Hash)]
 | 
			
		||||
pub(crate) enum Bias {
 | 
			
		||||
    ConfirmationBias,
 | 
			
		||||
    TheHaloEffect,
 | 
			
		||||
    FundamentalAttributionError,
 | 
			
		||||
    InGroupBias,
 | 
			
		||||
    DunningKrugerEffect,
 | 
			
		||||
    BarnumEffect
 | 
			
		||||
}
 | 
			
		||||
impl Default for Bias {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Bias::ConfirmationBias
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
impl std::fmt::Display for Bias {
 | 
			
		||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
			
		||||
        match self {
 | 
			
		||||
            Bias::ConfirmationBias => write!(f, "Confirmation Bias"),
 | 
			
		||||
            Bias::TheHaloEffect => write!(f, "The Halo Effect"),
 | 
			
		||||
            Bias::FundamentalAttributionError => write!(f, "Fundamental Attribution Error"),
 | 
			
		||||
            Bias::InGroupBias => write!(f, "In Group Bias"),
 | 
			
		||||
            Bias::DunningKrugerEffect => write!(f, "Dunning Kruger Effect"),
 | 
			
		||||
            Bias::BarnumEffect => write!(f, "Barnum Effect"),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
impl Bias {
 | 
			
		||||
    pub(crate) fn get_filename(&self) -> String {
 | 
			
		||||
        match self {
 | 
			
		||||
            Bias::ConfirmationBias => "confirmation_bias".to_string(),
 | 
			
		||||
            Bias::TheHaloEffect => "the_halo_effect".to_string(),
 | 
			
		||||
            Bias::FundamentalAttributionError => "fundamental_attribution_error".to_string(),
 | 
			
		||||
            Bias::InGroupBias => "in_group_bias".to_string(),
 | 
			
		||||
            Bias::DunningKrugerEffect => "dunning_kruger_effect".to_string(),
 | 
			
		||||
            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()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
/// Appraisal module, which includes various lessons related to the evaluation and appraisal of arguments.
 | 
			
		||||
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Hash)]
 | 
			
		||||
pub(crate) enum Appraisal {
 | 
			
		||||
    ConversionToPropositional, 
 | 
			
		||||
    CounterArgument,
 | 
			
		||||
}
 | 
			
		||||
impl Default for Appraisal {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Appraisal::ConversionToPropositional
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
impl std::fmt::Display for Appraisal {
 | 
			
		||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
			
		||||
        match self {
 | 
			
		||||
            Appraisal::ConversionToPropositional => write!(f, "Conversion to Propositional"),
 | 
			
		||||
            Appraisal::CounterArgument => write!(f, "Counter Argument"),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
impl Appraisal {
 | 
			
		||||
    pub(crate) fn get_filename(&self) -> String {
 | 
			
		||||
        match self {
 | 
			
		||||
            Appraisal::ConversionToPropositional => "conversion_to_propositional".to_string(),
 | 
			
		||||
            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()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user