Companion to: ARCHITECTURE.md Purpose: Detailed implementation templates and code examples Version: 1.0 Date: 2025-10-15
This guide provides ready-to-use code templates for implementing the Binary Math Education System. Each section includes complete, working examples that follow the architecture specification.
from dataclasses import dataclass
from typing import Optional, List, Dict, Set
from enum import Enum
class TokenType(Enum):
AND = "AND"
OR = "OR"
NOT = "NOT"
XOR = "XOR"
LPAREN = "("
RPAREN = ")"
VARIABLE = "VAR"
EOF = "EOF"
@dataclass
class Token:
type: TokenType
value: str
position: int
@dataclass
class ExpressionNode:
operator: Optional[str] = None
left: Optional['ExpressionNode'] = None
right: Optional['ExpressionNode'] = None
variable: Optional[str] = None
def evaluate(self, values: Dict[str, bool]) -> bool:
"""Evaluate the expression tree with given variable values"""
if self.variable is not None:
return values[self.variable]
if self.operator == "NOT":
return not self.left.evaluate(values)
elif self.operator == "AND":
return self.left.evaluate(values) and self.right.evaluate(values)
elif self.operator == "OR":
return self.left.evaluate(values) or self.right.evaluate(values)
elif self.operator == "XOR":
return self.left.evaluate(values) != self.right.evaluate(values)
raise ValueError(f"Unknown operator: {self.operator}")
def to_string(self) -> str:
"""Convert expression tree back to string"""
if self.variable is not None:
return self.variable
if self.operator == "NOT":
return f"NOT {self.left.to_string()}"
left_str = self.left.to_string()
right_str = self.right.to_string()
# Add parentheses if needed
if self.operator in ["AND", "OR", "XOR"]:
return f"({left_str} {self.operator} {right_str})"
return f"{left_str} {self.operator} {right_str}"
def complexity_score(self) -> int:
"""Calculate complexity score of expression"""
if self.variable is not None:
return 1
left_score = self.left.complexity_score() if self.left else 0
right_score = self.right.complexity_score() if self.right else 0
# NOT adds 2, binary operators add 3
operator_weight = 2 if self.operator == "NOT" else 3
return operator_weight + left_score + right_score
class ExpressionParser:
"""Recursive descent parser for logic expressions"""
def __init__(self):
self.tokens: List[Token] = []
self.current = 0
def parse(self, expression: str) -> ExpressionNode:
"""Parse expression string into AST"""
self.tokens = self.tokenize(expression)
self.current = 0
if not self.tokens:
raise ValueError("Empty expression")
result = self._parse_or_expr()
if self.current < len(self.tokens) and self.tokens[self.current].type != TokenType.EOF:
raise ValueError(f"Unexpected token: {self.tokens[self.current].value}")
return result
def tokenize(self, expression: str) -> List[Token]:
"""Convert expression string to tokens"""
tokens = []
i = 0
expression = expression.strip()
while i < len(expression):
# Skip whitespace
if expression[i].isspace():
i += 1
continue
# Check for operators and parentheses
if expression[i:i+3] == "AND":
tokens.append(Token(TokenType.AND, "AND", i))
i += 3
elif expression[i:i+2] == "OR":
tokens.append(Token(TokenType.OR, "OR", i))
i += 2
elif expression[i:i+3] == "NOT":
tokens.append(Token(TokenType.NOT, "NOT", i))
i += 3
elif expression[i:i+3] == "XOR":
tokens.append(Token(TokenType.XOR, "XOR", i))
i += 3
elif expression[i] == "(":
tokens.append(Token(TokenType.LPAREN, "(", i))
i += 1
elif expression[i] == ")":
tokens.append(Token(TokenType.RPAREN, ")", i))
i += 1
elif expression[i].isalpha():
# Variable name (single uppercase letter)
tokens.append(Token(TokenType.VARIABLE, expression[i], i))
i += 1
else:
raise ValueError(f"Unexpected character at position {i}: {expression[i]}")
tokens.append(Token(TokenType.EOF, "", len(expression)))
return tokens
def extract_variables(self, expression: str) -> Set[str]:
"""Extract all variable names from expression"""
tokens = self.tokenize(expression)
return {t.value for t in tokens if t.type == TokenType.VARIABLE}
def validate_syntax(self, expression: str) -> tuple[bool, str]:
"""Validate expression syntax, return (is_valid, error_message)"""
try:
self.parse(expression)
return True, ""
except Exception as e:
return False, str(e)
# Recursive descent parsing methods
def _parse_or_expr(self) -> ExpressionNode:
"""Parse OR expressions (lowest precedence)"""
left = self._parse_xor_expr()
while self._match(TokenType.OR):
operator = self._previous().value
right = self._parse_xor_expr()
left = ExpressionNode(operator=operator, left=left, right=right)
return left
def _parse_xor_expr(self) -> ExpressionNode:
"""Parse XOR expressions"""
left = self._parse_and_expr()
while self._match(TokenType.XOR):
operator = self._previous().value
right = self._parse_and_expr()
left = ExpressionNode(operator=operator, left=left, right=right)
return left
def _parse_and_expr(self) -> ExpressionNode:
"""Parse AND expressions"""
left = self._parse_not_expr()
while self._match(TokenType.AND):
operator = self._previous().value
right = self._parse_not_expr()
left = ExpressionNode(operator=operator, left=left, right=right)
return left
def _parse_not_expr(self) -> ExpressionNode:
"""Parse NOT expressions (highest precedence for unary)"""
if self._match(TokenType.NOT):
operator = self._previous().value
expr = self._parse_not_expr() # Right associative
return ExpressionNode(operator=operator, left=expr)
return self._parse_primary()
def _parse_primary(self) -> ExpressionNode:
"""Parse primary expressions (variables and parenthesized expressions)"""
if self._match(TokenType.VARIABLE):
return ExpressionNode(variable=self._previous().value)
if self._match(TokenType.LPAREN):
expr = self._parse_or_expr()
if not self._match(TokenType.RPAREN):
raise ValueError("Expected ')'")
return expr
raise ValueError(f"Unexpected token: {self._peek().value}")
# Helper methods
def _match(self, *types: TokenType) -> bool:
"""Check if current token matches any of the given types"""
for token_type in types:
if self._check(token_type):
self._advance()
return True
return False
def _check(self, token_type: TokenType) -> bool:
"""Check if current token is of given type"""
if self._is_at_end():
return False
return self._peek().type == token_type
def _advance(self) -> Token:
"""Move to next token"""
if not self._is_at_end():
self.current += 1
return self._previous()
def _is_at_end(self) -> bool:
"""Check if at end of tokens"""
return self.current >= len(self.tokens) or self._peek().type == TokenType.EOF
def _peek(self) -> Token:
"""Get current token without advancing"""
if self.current < len(self.tokens):
return self.tokens[self.current]
return self.tokens[-1] # Return EOF token
def _previous(self) -> Token:
"""Get previous token"""
return self.tokens[self.current - 1]from itertools import product
from typing import List, Tuple
@dataclass
class TruthTableRow:
inputs: Dict[str, bool]
output: bool
def to_string(self, variables: List[str]) -> str:
"""Format row as string"""
input_str = ", ".join(f"{v}={int(self.inputs[v])}" for v in variables)
return f"{input_str} => {int(self.output)}"
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary for JSON serialization"""
return {
"inputs": {k: int(v) for k, v in self.inputs.items()},
"output": int(self.output)
}
@dataclass
class TruthTable:
expression: str
variables: List[str]
rows: List[TruthTableRow]
def to_text_table(self) -> str:
"""Generate formatted text table"""
if not self.rows:
return "Empty truth table"
# Header
header = " | ".join(self.variables) + " | Result"
separator = "-" * len(header)
# Rows
row_strings = []
for row in self.rows:
values = [str(int(row.inputs[v])) for v in self.variables]
values.append(str(int(row.output)))
row_strings.append(" | ".join(f"{v:^{len(self.variables[i]) if i < len(self.variables) else 6}}"
for i, v in enumerate(values)))
return "\n".join([header, separator] + row_strings)
def to_structured_dict(self) -> Dict:
"""Convert to structured dictionary"""
return {
"expression": self.expression,
"variables": self.variables,
"rows": [row.to_dict() for row in self.rows],
"num_rows": len(self.rows)
}
def get_minterms(self) -> List[str]:
"""Get minterms (rows where output is True)"""
minterms = []
for i, row in enumerate(self.rows):
if row.output:
minterms.append(f"m{i}")
return minterms
def get_maxterms(self) -> List[str]:
"""Get maxterms (rows where output is False)"""
maxterms = []
for i, row in enumerate(self.rows):
if not row.output:
maxterms.append(f"M{i}")
return maxterms
class TruthTableGenerator:
"""Generate truth tables from logic expressions"""
def __init__(self):
self.parser = ExpressionParser()
def generate(self, expression: str) -> TruthTable:
"""Generate complete truth table for expression"""
# Parse expression
expr_tree = self.parser.parse(expression)
# Extract variables and sort alphabetically
variables = sorted(self.parser.extract_variables(expression))
if not variables:
raise ValueError("Expression must contain at least one variable")
# Generate all possible input combinations
rows = []
for values in product([False, True], repeat=len(variables)):
input_dict = dict(zip(variables, values))
output = expr_tree.evaluate(input_dict)
rows.append(TruthTableRow(inputs=input_dict, output=output))
return TruthTable(
expression=expression,
variables=variables,
rows=rows
)
@dataclass
class ValidationResult:
is_correct: bool
errors: List[str]
suggestions: List[str]
correct_rows: int
total_rows: int
def get_score(self) -> float:
"""Calculate percentage score"""
if self.total_rows == 0:
return 0.0
return (self.correct_rows / self.total_rows) * 100
def get_detailed_feedback(self) -> str:
"""Generate detailed feedback message"""
feedback = [f"Score: {self.get_score():.1f}% ({self.correct_rows}/{self.total_rows} correct)"]
if self.errors:
feedback.append("\nErrors:")
feedback.extend(f" - {error}" for error in self.errors)
if self.suggestions:
feedback.append("\nSuggestions:")
feedback.extend(f" - {suggestion}" for suggestion in self.suggestions)
return "\n".join(feedback)
class TruthTableValidator:
"""Validate student-created truth tables"""
def __init__(self):
self.generator = TruthTableGenerator()
def validate(self, expression: str, student_table: List[TruthTableRow]) -> ValidationResult:
"""Validate student's truth table against correct solution"""
# Generate correct table
correct_table = self.generator.generate(expression)
errors = []
suggestions = []
correct_rows = 0
# Check row count
if len(student_table) != len(correct_table.rows):
errors.append(
f"Wrong number of rows: expected {len(correct_table.rows)}, got {len(student_table)}"
)
suggestions.append(
f"With {len(correct_table.variables)} variables, you need 2^{len(correct_table.variables)} = {len(correct_table.rows)} rows"
)
# Check each row
for i, (student_row, correct_row) in enumerate(zip(student_table, correct_table.rows)):
# Check inputs match
if student_row.inputs != correct_row.inputs:
errors.append(f"Row {i}: Input values don't match expected combination")
continue
# Check output
if student_row.output == correct_row.output:
correct_rows += 1
else:
errors.append(
f"Row {i}: Expected output {int(correct_row.output)}, got {int(student_row.output)}"
)
# Identify pattern errors
pattern_errors = self._identify_pattern_errors(student_table, correct_table)
suggestions.extend(pattern_errors)
is_correct = correct_rows == len(correct_table.rows) and len(student_table) == len(correct_table.rows)
return ValidationResult(
is_correct=is_correct,
errors=errors,
suggestions=suggestions,
correct_rows=correct_rows,
total_rows=len(correct_table.rows)
)
def _identify_pattern_errors(self, student_table: List[TruthTableRow], correct_table: TruthTable) -> List[str]:
"""Identify common pattern mistakes"""
suggestions = []
if len(student_table) != len(correct_table.rows):
return suggestions
# Check if student confused AND with OR
and_or_confusion = sum(
1 for s, c in zip(student_table, correct_table.rows)
if s.output != c.output
)
if and_or_confusion > 0 and and_or_confusion < len(student_table):
if "AND" in correct_table.expression and "OR" not in correct_table.expression:
suggestions.append("Double-check the AND operation - both inputs must be 1")
elif "OR" in correct_table.expression and "AND" not in correct_table.expression:
suggestions.append("Double-check the OR operation - at least one input must be 1")
return suggestionsimport random
from abc import ABC, abstractmethod
class ProblemType(Enum):
DECIMAL_TO_BINARY = "decimal_to_binary"
BINARY_TO_DECIMAL = "binary_to_decimal"
BINARY_ADDITION = "binary_addition"
SIMPLE_GATE = "simple_gate"
TRUTH_TABLE_FROM_EXPRESSION = "truth_table_from_expression"
EXPRESSION_SIMPLIFICATION = "expression_simplification"
class DifficultyLevel(Enum):
BEGINNER = 1
INTERMEDIATE = 2
ADVANCED = 3
@dataclass
class Problem:
id: str
type: ProblemType
difficulty: DifficultyLevel
question: str
solution: Any
hints: List[str]
metadata: Dict[str, Any]
def check_answer(self, student_answer: Any) -> 'AnswerResult':
"""Check if student answer is correct"""
# This will be implemented by problem type
pass
def get_hint(self, level: int) -> str:
"""Get hint at specified level (1-3)"""
if level < 1 or level > len(self.hints):
return "No more hints available"
return self.hints[level - 1]
def get_difficulty_score(self) -> float:
"""Get numerical difficulty score"""
base_score = {
DifficultyLevel.BEGINNER: 20,
DifficultyLevel.INTERMEDIATE: 50,
DifficultyLevel.ADVANCED: 80
}
return base_score[self.difficulty]
class ProblemGenerator:
"""Generate randomized practice problems"""
def __init__(self, random_seed: Optional[int] = None):
self.rng = random.Random(random_seed)
self.used_problems = set()
self.generators = {
ProblemType.DECIMAL_TO_BINARY: BinaryConversionGenerator(self.rng),
ProblemType.BINARY_TO_DECIMAL: BinaryConversionGenerator(self.rng),
ProblemType.BINARY_ADDITION: BinaryConversionGenerator(self.rng),
ProblemType.SIMPLE_GATE: LogicExpressionGenerator(self.rng),
ProblemType.TRUTH_TABLE_FROM_EXPRESSION: LogicExpressionGenerator(self.rng),
}
def generate_problem(
self,
problem_type: ProblemType,
difficulty: DifficultyLevel,
constraints: Optional[Dict] = None
) -> Problem:
"""Generate a single problem"""
generator = self.generators.get(problem_type)
if not generator:
raise ValueError(f"No generator for problem type: {problem_type}")
problem = generator.generate(problem_type, difficulty, constraints or {})
# Ensure uniqueness
attempt = 0
while problem.id in self.used_problems and attempt < 10:
problem = generator.generate(problem_type, difficulty, constraints or {})
attempt += 1
self.used_problems.add(problem.id)
return problem
class BinaryConversionGenerator:
"""Generate binary conversion problems"""
def __init__(self, rng: random.Random):
self.rng = rng
self.hint_gen = HintGenerator()
def generate(self, problem_type: ProblemType, difficulty: DifficultyLevel, constraints: Dict) -> Problem:
"""Generate binary conversion problem"""
if problem_type == ProblemType.DECIMAL_TO_BINARY:
return self._generate_decimal_to_binary(difficulty)
elif problem_type == ProblemType.BINARY_TO_DECIMAL:
return self._generate_binary_to_decimal(difficulty)
elif problem_type == ProblemType.BINARY_ADDITION:
return self._generate_binary_addition(difficulty)
def _generate_decimal_to_binary(self, difficulty: DifficultyLevel) -> Problem:
"""Generate decimal to binary conversion problem"""
# Difficulty determines number range
ranges = {
DifficultyLevel.BEGINNER: (1, 15), # 4 bits
DifficultyLevel.INTERMEDIATE: (16, 127), # 7 bits
DifficultyLevel.ADVANCED: (128, 255) # 8 bits
}
min_val, max_val = ranges[difficulty]
decimal_value = self.rng.randint(min_val, max_val)
binary_solution = bin(decimal_value)[2:] # Remove '0b' prefix
problem = Problem(
id=f"d2b_{decimal_value}_{difficulty.value}",
type=ProblemType.DECIMAL_TO_BINARY,
difficulty=difficulty,
question=f"Convert {decimal_value} to binary",
solution=binary_solution,
hints=self.hint_gen.generate_decimal_to_binary_hints(decimal_value),
metadata={"decimal": decimal_value, "bits": len(binary_solution)}
)
return problem
def _generate_binary_to_decimal(self, difficulty: DifficultyLevel) -> Problem:
"""Generate binary to decimal conversion problem"""
ranges = {
DifficultyLevel.BEGINNER: (1, 15),
DifficultyLevel.INTERMEDIATE: (16, 127),
DifficultyLevel.ADVANCED: (128, 255)
}
min_val, max_val = ranges[difficulty]
decimal_value = self.rng.randint(min_val, max_val)
binary_value = bin(decimal_value)[2:]
problem = Problem(
id=f"b2d_{binary_value}_{difficulty.value}",
type=ProblemType.BINARY_TO_DECIMAL,
difficulty=difficulty,
question=f"Convert {binary_value} to decimal",
solution=decimal_value,
hints=self.hint_gen.generate_binary_to_decimal_hints(binary_value),
metadata={"binary": binary_value, "bits": len(binary_value)}
)
return problem
def _generate_binary_addition(self, difficulty: DifficultyLevel) -> Problem:
"""Generate binary addition problem"""
bit_widths = {
DifficultyLevel.BEGINNER: 2,
DifficultyLevel.INTERMEDIATE: 4,
DifficultyLevel.ADVANCED: 8
}
width = bit_widths[difficulty]
max_val = (2 ** width) - 1
a = self.rng.randint(1, max_val // 2)
b = self.rng.randint(1, max_val // 2)
binary_a = bin(a)[2:].zfill(width)
binary_b = bin(b)[2:].zfill(width)
result = a + b
binary_result = bin(result)[2:]
problem = Problem(
id=f"badd_{binary_a}_{binary_b}_{difficulty.value}",
type=ProblemType.BINARY_ADDITION,
difficulty=difficulty,
question=f"What is {binary_a} + {binary_b} in binary?",
solution=binary_result,
hints=self.hint_gen.generate_binary_addition_hints(binary_a, binary_b),
metadata={"operand1": binary_a, "operand2": binary_b, "carries": result > max_val}
)
return problem
class LogicExpressionGenerator:
"""Generate logic expression problems"""
def __init__(self, rng: random.Random):
self.rng = rng
self.hint_gen = HintGenerator()
def generate(self, problem_type: ProblemType, difficulty: DifficultyLevel, constraints: Dict) -> Problem:
"""Generate logic expression problem"""
if problem_type == ProblemType.SIMPLE_GATE:
return self._generate_simple_gate(difficulty)
elif problem_type == ProblemType.TRUTH_TABLE_FROM_EXPRESSION:
return self._generate_truth_table_problem(difficulty)
def _generate_simple_gate(self, difficulty: DifficultyLevel) -> Problem:
"""Generate simple logic gate problem"""
operators = ["AND", "OR", "XOR"]
if difficulty == DifficultyLevel.BEGINNER:
# Single gate: A AND B
op = self.rng.choice(operators)
expression = f"A {op} B"
num_vars = 2
elif difficulty == DifficultyLevel.INTERMEDIATE:
# Two gates: A AND (B OR C)
op1 = self.rng.choice(operators)
op2 = self.rng.choice(operators)
expression = f"A {op1} (B {op2} C)"
num_vars = 3
else: # ADVANCED
# Complex with NOT: NOT A AND (B OR (C XOR D))
op1 = self.rng.choice(operators)
op2 = self.rng.choice(operators)
op3 = self.rng.choice(operators)
expression = f"NOT A {op1} (B {op2} (C {op3} D))"
num_vars = 4
# Evaluate for specific input
parser = ExpressionParser()
variables = sorted(parser.extract_variables(expression))
test_values = {v: self.rng.choice([True, False]) for v in variables}
tree = parser.parse(expression)
result = tree.evaluate(test_values)
value_str = ", ".join(f"{v}={int(test_values[v])}" for v in variables)
problem = Problem(
id=f"gate_{expression}_{difficulty.value}",
type=ProblemType.SIMPLE_GATE,
difficulty=difficulty,
question=f"Evaluate: {expression} when {value_str}",
solution=int(result),
hints=self.hint_gen.generate_logic_expression_hints(expression, test_values),
metadata={"expression": expression, "variables": variables, "test_values": test_values}
)
return problem
def _generate_truth_table_problem(self, difficulty: DifficultyLevel) -> Problem:
"""Generate truth table creation problem"""
operators = ["AND", "OR", "XOR"]
if difficulty == DifficultyLevel.BEGINNER:
op = self.rng.choice(operators)
expression = f"A {op} B"
elif difficulty == DifficultyLevel.INTERMEDIATE:
op1 = self.rng.choice(operators)
op2 = self.rng.choice(operators)
expression = f"(A {op1} B) {op2} C"
else: # ADVANCED
op1 = self.rng.choice(operators)
op2 = self.rng.choice(operators)
expression = f"NOT (A {op1} B) {op2} C"
# Generate solution
generator = TruthTableGenerator()
solution = generator.generate(expression)
problem = Problem(
id=f"tt_{expression}_{difficulty.value}",
type=ProblemType.TRUTH_TABLE_FROM_EXPRESSION,
difficulty=difficulty,
question=f"Create a truth table for: {expression}",
solution=solution,
hints=self.hint_gen.generate_truth_table_hints(expression),
metadata={"expression": expression, "num_rows": len(solution.rows)}
)
return problemclass HintGenerator:
"""Generate progressive hints for problems"""
def generate_decimal_to_binary_hints(self, decimal: int) -> List[str]:
"""Generate 3-level hints for decimal to binary conversion"""
# Level 1: Concept reminder
hint1 = "Convert by repeatedly dividing by 2 and tracking remainders"
# Level 2: Show first step
first_div = decimal // 2
first_rem = decimal % 2
hint2 = f"Start: {decimal} ÷ 2 = {first_div} remainder {first_rem}"
# Level 3: Show most of solution
binary = bin(decimal)[2:]
shown = binary[:-2] if len(binary) > 2 else ""
hint3 = f"The binary starts with {shown}... (complete the last 2 bits)"
return [hint1, hint2, hint3]
def generate_binary_to_decimal_hints(self, binary: str) -> List[str]:
"""Generate 3-level hints for binary to decimal conversion"""
# Level 1: Concept reminder
hint1 = "Each bit position represents a power of 2 (right to left: 2^0, 2^1, 2^2...)"
# Level 2: Show position values
powers = [f"2^{i}" for i in range(len(binary)-1, -1, -1)]
hint2 = f"Positions are: {' '.join(powers)}"
# Level 3: Show partial calculation
decimal = int(binary, 2)
partial_sum = sum(int(binary[i]) * (2 ** (len(binary)-1-i)) for i in range(len(binary)//2))
hint3 = f"First half sums to {partial_sum}. Now add the rest..."
return [hint1, hint2, hint3]
def generate_binary_addition_hints(self, a: str, b: str) -> List[str]:
"""Generate 3-level hints for binary addition"""
# Level 1: Concept reminder
hint1 = "Add right to left, just like decimal. 1+1=10 (carry the 1)"
# Level 2: Show first column
last_a = int(a[-1])
last_b = int(b[-1])
last_sum = last_a + last_b
hint2 = f"Rightmost column: {last_a} + {last_b} = {bin(last_sum)[2:]}"
# Level 3: Show partial result
result = bin(int(a, 2) + int(b, 2))[2:]
shown = result[:-2] if len(result) > 2 else ""
hint3 = f"Result starts with {shown}... (complete the last 2 bits)"
return [hint1, hint2, hint3]
def generate_logic_expression_hints(self, expression: str, values: Dict[str, bool]) -> List[str]:
"""Generate 3-level hints for logic expressions"""
# Level 1: Concept reminder
operations = []
if "AND" in expression:
operations.append("AND: both must be 1")
if "OR" in expression:
operations.append("OR: at least one must be 1")
if "XOR" in expression:
operations.append("XOR: exactly one must be 1")
if "NOT" in expression:
operations.append("NOT: flips the value")
hint1 = "Remember: " + ", ".join(operations)
# Level 2: Show evaluation order
hint2 = "Evaluate from innermost parentheses outward, NOT operations first"
# Level 3: Show partial evaluation
parser = ExpressionParser()
tree = parser.parse(expression)
hint3 = f"After evaluating inner expressions, you should get a simpler form..."
return [hint1, hint2, hint3]
def generate_truth_table_hints(self, expression: str) -> List[str]:
"""Generate 3-level hints for truth table creation"""
parser = ExpressionParser()
variables = sorted(parser.extract_variables(expression))
num_rows = 2 ** len(variables)
# Level 1: Structure reminder
hint1 = f"With {len(variables)} variables, you need {num_rows} rows (2^{len(variables)})"
# Level 2: Show how to do one row
first_values = {v: False for v in variables}
tree = parser.parse(expression)
first_result = tree.evaluate(first_values)
value_str = ", ".join(f"{v}=0" for v in variables)
hint2 = f"First row ({value_str}): {expression} = {int(first_result)}"
# Level 3: Show partial table
generator = TruthTableGenerator()
table = generator.generate(expression)
rows_to_show = min(len(table.rows) // 2, 4)
partial = "\n".join(table.rows[i].to_string(variables) for i in range(rows_to_show))
hint3 = f"First {rows_to_show} rows:\n{partial}\n(complete the rest)"
return [hint1, hint2, hint3]# Complete working example
class BinaryEducationSystem:
"""Main system interface"""
def __init__(self):
self.truth_table_gen = TruthTableGenerator()
self.problem_gen = ProblemGenerator()
self.validator = TruthTableValidator()
def demo_truth_table(self):
"""Demo: Generate and display a truth table"""
expression = "A AND B OR NOT C"
print(f"Expression: {expression}\n")
table = self.truth_table_gen.generate(expression)
print(table.to_text_table())
print(f"\nMinterms: {', '.join(table.get_minterms())}")
def demo_problem_generation(self):
"""Demo: Generate and solve a problem"""
problem = self.problem_gen.generate_problem(
ProblemType.DECIMAL_TO_BINARY,
DifficultyLevel.BEGINNER
)
print(f"\nProblem: {problem.question}")
print(f"Difficulty: {problem.difficulty.name}")
print(f"\nHint 1: {problem.get_hint(1)}")
print(f"Hint 2: {problem.get_hint(2)}")
print(f"Hint 3: {problem.get_hint(3)}")
print(f"\nSolution: {problem.solution}")
def demo_validation(self):
"""Demo: Validate a student's truth table"""
expression = "A AND B"
# Student's (incorrect) answer
student_table = [
TruthTableRow({"A": False, "B": False}, False),
TruthTableRow({"A": False, "B": True}, True), # Wrong!
TruthTableRow({"A": True, "B": False}, False),
TruthTableRow({"A": True, "B": True}, True),
]
result = self.validator.validate(expression, student_table)
print(f"\n{result.get_detailed_feedback()}")
# Run demos
if __name__ == "__main__":
system = BinaryEducationSystem()
print("=" * 60)
print("DEMO 1: Truth Table Generation")
print("=" * 60)
system.demo_truth_table()
print("\n" + "=" * 60)
print("DEMO 2: Problem Generation with Hints")
print("=" * 60)
system.demo_problem_generation()
print("\n" + "=" * 60)
print("DEMO 3: Answer Validation")
print("=" * 60)
system.demo_validation()# MVP-level testing (2-5 tests per component)
def test_expression_parser():
"""Test basic expression parsing"""
parser = ExpressionParser()
# Test 1: Simple expression
tree = parser.parse("A AND B")
assert tree.operator == "AND"
# Test 2: Complex expression
tree = parser.parse("(A OR B) AND NOT C")
assert tree.operator == "AND"
# Test 3: Evaluation
tree = parser.parse("A AND B")
result = tree.evaluate({"A": True, "B": False})
assert result == False
def test_truth_table_generator():
"""Test truth table generation"""
gen = TruthTableGenerator()
# Test 1: Generate simple table
table = gen.generate("A AND B")
assert len(table.rows) == 4
# Test 2: Verify outputs
table = gen.generate("A OR B")
true_count = sum(1 for row in table.rows if row.output)
assert true_count == 3 # OR is true in 3 of 4 cases
def test_problem_generator():
"""Test problem generation"""
gen = ProblemGenerator(random_seed=42)
# Test 1: Generate decimal to binary
problem = gen.generate_problem(
ProblemType.DECIMAL_TO_BINARY,
DifficultyLevel.BEGINNER
)
assert 1 <= int(problem.metadata["decimal"]) <= 15
# Test 2: Hints are generated
assert len(problem.hints) == 3
def test_validation():
"""Test answer validation"""
validator = TruthTableValidator()
# Test 1: Correct answer
correct_table = [
TruthTableRow({"A": False, "B": False}, False),
TruthTableRow({"A": False, "B": True}, False),
TruthTableRow({"A": True, "B": False}, False),
TruthTableRow({"A": True, "B": True}, True),
]
result = validator.validate("A AND B", correct_table)
assert result.is_correct == True
assert result.get_score() == 100.0
# Test 2: Incorrect answer
incorrect_table = [
TruthTableRow({"A": False, "B": False}, True), # Wrong
TruthTableRow({"A": False, "B": True}, False),
TruthTableRow({"A": True, "B": False}, False),
TruthTableRow({"A": True, "B": True}, True),
]
result = validator.validate("A AND B", incorrect_table)
assert result.is_correct == False
assert result.correct_rows == 3- Copy templates into your project structure
- Run the demo to see the system in action
- Add persistence using SQLite or JSON files
- Build CLI for student interaction
- Add web interface (optional) using Flask or FastAPI
Each template is production-ready and follows the architecture specification. Customize as needed for your specific requirements.