/**
* Base Explanation Generator
* Abstract base class for all logic system explanation generators
* Eliminates massive code duplication across specific generators
*/
import {
SolutionStep
} from '../types.js';
import {
EnhancedSolutionStep,
EnhancedLogicalSolution,
ExplanationOptions,
ProofContext,
ExplanationGenerator
} from '../types-explanations.js';
/**
* Abstract base class for explanation generators.
* Provides common functionality for all logic systems.
*/
export abstract class BaseExplanationGenerator implements ExplanationGenerator {
/**
* Get rule explanations specific to the logic system
*/
protected abstract getRuleExplanations(): Record<string, string>;
/**
* Get symbol explanations specific to the logic system
*/
protected abstract getSymbolExplanations(): Record<string, string>;
/**
* Get specific explanation based on the step and context
*/
protected abstract getSpecificExplanation(step: SolutionStep, context: ProofContext): string;
/**
* Generate system-specific visualization
*/
protected abstract generateSystemSpecificVisualization(step: SolutionStep, context: ProofContext): string;
/**
* Get example of how a rule is applied
*/
protected abstract getRuleExample(rule: string): string;
/**
* Get historical context for a rule (optional)
*/
protected getHistoricalContext(rule: string): string {
return '';
}
/**
* Generate a detailed explanation for a proof step.
* @param step The solution step to explain
* @param context The proof context
* @param options Explanation options
* @returns Detailed explanation of the step
*/
generateStepExplanation(
step: SolutionStep,
context: ProofContext,
options: ExplanationOptions
): string {
const { detailLevel = 2 } = options;
// Basic explanation for any step
let explanation = `Step ${step.index}: ${step.statement}\n`;
explanation += `Rule applied: ${step.rule}\n`;
explanation += `Justification: ${step.justification}\n\n`;
// Add more detailed explanation based on the rule
if (detailLevel >= 2) {
const ruleDetails = this.getRuleDetails(step.rule);
if (ruleDetails) {
explanation += `${ruleDetails}\n\n`;
}
// Add specific explanation based on the step
if (this.isPremise(step, context)) {
explanation += this.getPremiseExplanation(step, context);
} else {
explanation += `This step follows from the previous steps by applying the rule "${step.rule}". `;
explanation += this.getDerivationExplanation(step, context);
}
}
// Add even more detailed explanation for highest detail level
if (detailLevel >= 3) {
explanation += this.getDetailedExplanation(step, context);
}
return explanation;
}
/**
* Generate visual representation for a step.
* @param step The solution step
* @param context The proof context
* @returns Visual representation of the step
*/
generateVisualization(
step: SolutionStep,
context: ProofContext
): string {
return this.generateSystemSpecificVisualization(step, context);
}
/**
* Generate educational notes for a logical rule.
* @param rule The logical rule
* @param context The proof context
* @returns Educational notes about the rule
*/
generateEducationalNotes(
rule: string,
context: ProofContext
): string {
const ruleExplanations = this.getRuleExplanations();
// Check if we have specific notes for this rule
if (ruleExplanations[rule]) {
let notes = ruleExplanations[rule] + '\n\n';
// Add examples of the rule
notes += this.getRuleExample(rule);
// Add historical context if available
notes += this.getHistoricalContext(rule);
return notes;
}
// Default explanation
return `${rule} is a rule in ${this.getLogicSystemName()} used for formal proofs and logical deduction.`;
}
/**
* Determine the difficulty level of a step.
* @param step The solution step
* @param context The proof context
* @returns Difficulty level (1-5)
*/
determineDifficulty(
step: SolutionStep,
context: ProofContext
): number {
// Premises are easy to understand
if (this.isPremise(step, context)) {
return 1;
}
// Override in subclasses for specific difficulty assessment
return this.assessRuleDifficulty(step.rule);
}
/**
* Enhance a solution with detailed explanations.
* @param steps The solution steps
* @param context The proof context
* @param options Explanation options
* @returns Enhanced solution with detailed explanations
*/
enhanceSolution(
steps: SolutionStep[],
context: ProofContext,
options: ExplanationOptions
): EnhancedLogicalSolution {
const enhancedSteps: EnhancedSolutionStep[] = [];
// Create updated context with steps added progressively
const updatedContext: ProofContext = {
...context,
previousSteps: []
};
// Enhance each step
for (const step of steps) {
// Update context with previous steps
updatedContext.previousSteps = [...updatedContext.previousSteps, step];
// Create enhanced step
const enhancedStep: EnhancedSolutionStep = {
index: step.index,
statement: step.statement,
rule: step.rule,
justification: step.justification,
detailedExplanation: this.generateStepExplanation(step, updatedContext, options),
dependencies: this.getDependencies(step),
difficulty: this.determineDifficulty(step, updatedContext)
};
// Add visualization if enabled
if (options.includeVisualizations) {
enhancedStep.visualization = this.generateVisualization(step, updatedContext);
}
// Add educational notes if enabled
if (options.includeEducationalNotes) {
enhancedStep.educationalNotes = this.generateEducationalNotes(step.rule, updatedContext);
}
enhancedSteps.push(enhancedStep);
}
// Create enhanced solution
const lastStep = steps[steps.length - 1];
const conclusion = lastStep ? lastStep.statement : '';
return {
steps: enhancedSteps,
conclusion,
proofStrategy: this.generateProofStrategy(steps, context),
keyInsights: this.generateKeyInsights(steps, context),
alternativeApproaches: options.includeAlternatives
? this.generateAlternativeApproaches(steps, context)
: undefined
};
}
/**
* Get detailed explanation for a rule.
* @param rule The logical rule
* @returns Detailed explanation of the rule
*/
protected getRuleDetails(rule: string): string | null {
const ruleExplanations = this.getRuleExplanations();
return ruleExplanations[rule] || null;
}
/**
* Get explanation for how a step is derived from previous steps.
* @param step The solution step
* @param context The proof context
* @returns Explanation of the derivation
*/
protected getDerivationExplanation(step: SolutionStep, context: ProofContext): string {
// Get specific explanation from subclass
return this.getSpecificExplanation(step, context);
}
/**
* Get explanations for logical symbols in a statement.
* @param statement The logical statement
* @returns Explanation of the symbols
*/
protected getSymbolExplanationsForStatement(statement: string): string {
const symbolExplanations = this.getSymbolExplanations();
let explanation = '\nSymbols used in this statement:\n';
let foundSymbols = false;
for (const [symbol, desc] of Object.entries(symbolExplanations)) {
if (statement.includes(symbol)) {
explanation += `- ${symbol}: ${desc}\n`;
foundSymbols = true;
}
}
return foundSymbols ? explanation : '';
}
/**
* Get the dependencies for a step.
* @param step The solution step
* @returns Array of dependent step indices
*/
protected getDependencies(step: SolutionStep): number[] {
// Parse justification to find dependencies
// If this is a premise, no dependencies
if (step.rule === 'Premise' || step.rule === 'Major Premise' || step.rule === 'Minor Premise') {
return [];
}
// Look for step numbers in the justification
const dependencies: number[] = [];
const matches = step.justification.match(/step\s+(\d+)(?:\s*(?:,|and)\s*step\s+(\d+))?/gi);
if (matches) {
matches.forEach(match => {
const nums = match.match(/\d+/g);
if (nums) {
nums.forEach(num => dependencies.push(parseInt(num)));
}
});
}
// Remove duplicates
return [...new Set(dependencies)];
}
/**
* Generate a description of the overall proof strategy.
* Must be implemented by subclasses
*/
protected abstract generateProofStrategy(steps: SolutionStep[], context: ProofContext): string;
/**
* Generate key insights from the proof.
* Must be implemented by subclasses
*/
protected abstract generateKeyInsights(steps: SolutionStep[], context: ProofContext): string[];
/**
* Generate alternative approaches to the proof.
* @param steps The solution steps
* @param context The proof context
* @returns Array of alternative approaches
*/
protected generateAlternativeApproaches(steps: SolutionStep[], context: ProofContext): string[] {
// Default implementation - can be overridden by subclasses
return [
"Alternative approaches depend on the specific logic system and problem type.",
"Consult system-specific documentation for alternative proof methods."
];
}
/**
* Check if a step is a premise
* @param step The solution step
* @param context The proof context
* @returns True if the step is a premise
*/
protected isPremise(step: SolutionStep, context: ProofContext): boolean {
return step.rule === 'Premise' ||
step.rule === 'Major Premise' ||
step.rule === 'Minor Premise' ||
(step.index <= (context.premises?.length || 0));
}
/**
* Get explanation for a premise
* @param step The solution step
* @param context The proof context
* @returns Explanation for the premise
*/
protected getPremiseExplanation(step: SolutionStep, context: ProofContext): string {
return `This is a premise of the argument. Premises are statements that are assumed to be true, and from which we will derive the conclusion.\n\n`;
}
/**
* Get detailed explanation for highest detail level
* @param step The solution step
* @param context The proof context
* @returns Detailed explanation
*/
protected getDetailedExplanation(step: SolutionStep, context: ProofContext): string {
let explanation = this.getSymbolExplanationsForStatement(step.statement);
// Add system-specific details
explanation += this.getSystemSpecificDetails(step, context);
return explanation;
}
/**
* Get system-specific details for a step
* Can be overridden by subclasses
*/
protected getSystemSpecificDetails(step: SolutionStep, context: ProofContext): string {
return '';
}
/**
* Assess the difficulty of a specific rule
* Should be overridden by subclasses for specific assessment
*/
protected assessRuleDifficulty(rule: string): number {
// Default moderate difficulty
return 3;
}
/**
* Get the name of the logic system
* Must be implemented by subclasses
*/
protected abstract getLogicSystemName(): string;
}