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