Migrate to Gitea

This commit is contained in:
2025-04-17 15:53:45 -06:00
parent da86957915
commit 7a0b0cd465
46 changed files with 6083 additions and 0 deletions

3
src/course/mod.rs Normal file
View File

@@ -0,0 +1,3 @@
pub(crate) mod units;
pub(crate) mod story;
pub(crate) mod tracker;

218
src/course/story.rs Normal file
View 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
View 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
View 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()
}
}