From 3c5e13edd39be7389952992dbd9c511010befd45 Mon Sep 17 00:00:00 2001 From: gitea Date: Fri, 12 Dec 2025 14:13:57 +0100 Subject: [PATCH] readme --- README.md | 317 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 315 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bfff5df..3f24072 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,316 @@ -# decky_app +# Practice Engine + +A pure Dart package implementing spaced repetition, weighted selection, and practice tracking for question decks. This package provides a complete solution for building quiz and flashcard applications with intelligent question selection based on learning progress. + +## What It Does + +The Practice Engine helps you build applications where users practice questions (like flashcards or quizzes) with an intelligent system that: + +- **Adapts to learning progress**: Questions you struggle with appear more frequently, while mastered questions appear less often +- **Uses spaced repetition**: Even "known" questions occasionally reappear to reinforce memory +- **Tracks comprehensive statistics**: Monitor progress, streaks, and overall deck mastery +- **Provides flexible configuration**: Customize behavior per deck to match different learning styles + +The engine is designed to be pure Dart (no Flutter dependencies), making it suitable for any Dart application including CLI tools, web apps, and mobile applications. + +## Features + +- **Spaced Repetition**: Known questions have low but increasing probability of appearing based on time since last seen +- **Weighted Random Selection**: Questions are selected based on priority points and spaced repetition logic +- **Priority Scoring**: Questions gain priority when answered incorrectly and lose it when answered correctly +- **Question Streak Logic**: Tracks consecutive correct answers to determine when a question is "known" +- **Attempt/Quiz Logic**: Create and process practice attempts with comprehensive result tracking +- **Deck Progress Tracking**: Track overall deck progress with practice percentage +- **Deck Configuration**: Customizable settings per deck (streak threshold, attempt size, priority changes, etc.) +- **Manual Overrides**: Manually mark questions as known or needs practice +- **Reset Behavior**: Reset deck state with optional attempt count preservation + +## Installation + +Add this package to your `pubspec.yaml`: + +```yaml +dependencies: + practice_engine: + path: packages/practice_engine # For local development + # Or use pub.dev if published: + # practice_engine: ^1.0.0 +``` + +Then run: + +```bash +dart pub get +``` + +## Requirements + +- Dart SDK >= 3.0.0 + +## Usage + +### Creating a Deck + +A deck contains questions and configuration. Each question tracks its learning state (streaks, priority, known status). + +```dart +import 'package:practice_engine/practice_engine.dart'; + +// Configure deck behavior +final config = DeckConfig( + requiredConsecutiveCorrect: 3, // Need 3 correct in a row to mark as "known" + defaultAttemptSize: 10, // Default questions per practice session + priorityIncreaseOnIncorrect: 5, // Add 5 priority points when wrong + priorityDecreaseOnCorrect: 2, // Remove 2 priority points when correct + immediateFeedbackEnabled: true, // Show feedback immediately +); + +// Create questions +final questions = [ + Question( + id: 'q1', + prompt: 'What is 2+2?', + answers: ['3', '4', '5'], + correctAnswerIndex: 1, // Answer at index 1 is correct + ), + Question( + id: 'q2', + prompt: 'What is the capital of France?', + answers: ['London', 'Paris', 'Berlin'], + correctAnswerIndex: 1, + ), + // ... more questions +]; + +// Create the deck +final deck = Deck( + id: 'math-deck', + title: 'Math Basics', + description: 'Basic arithmetic questions', + questions: questions, + config: config, +); +``` + +### Creating and Processing an Attempt + +An attempt represents a practice session. The engine intelligently selects questions based on priority and spaced repetition. + +```dart +// Create attempt service (optionally with seed for reproducible randomness) +final attemptService = AttemptService(seed: 42); + +// Create a new practice attempt +final attempt = attemptService.createAttempt(deck: deck); +// Or specify custom attempt size: +// final attempt = attemptService.createAttempt(deck: deck, attemptSize: 5); + +// Display questions to user and collect answers +// answers maps questionId -> userSelectedAnswerIndex +final answers = { + 'q1': 1, // User selected answer at index 1 + 'q2': 0, // User selected answer at index 0 + // ... +}; + +// Process the attempt and get results +final result = attemptService.processAttempt( + deck: deck, + attempt: attempt, + answers: answers, +); + +// Access the updated deck (with learning progress applied) +final updatedDeck = result.updatedDeck; + +// Access attempt results +print('Score: ${result.result.percentageCorrect}%'); +print('Correct: ${result.result.correctCount}/${result.result.totalQuestions}'); +print('Deck progress: ${updatedDeck.practicePercentage.toStringAsFixed(1)}%'); +``` + +### Accessing Deck Statistics + +```dart +// Get overall deck statistics +print('Total questions: ${deck.numberOfQuestions}'); +print('Known questions: ${deck.knownCount}'); +print('Practice percentage: ${deck.practicePercentage}%'); +print('Current attempt index: ${deck.currentAttemptIndex}'); + +// Access individual question state +final question = deck.questions.firstWhere((q) => q.id == 'q1'); +print('Consecutive correct: ${question.consecutiveCorrect}'); +print('Priority points: ${question.priorityPoints}'); +print('Total attempts: ${question.totalAttempts}'); +print('Is known: ${question.isKnown}'); +``` + +### Manual Overrides + +Manually control question state when needed (e.g., user wants to mark something as learned). + +```dart +// Mark question as known (resets streak, sets isKnown=true) +final updatedDeck = DeckService.markQuestionAsKnown( + deck: deck, + questionId: 'q1', +); + +// Mark question as needs practice (resets streak, sets isKnown=false, adds priority) +final updatedDeck2 = DeckService.markQuestionAsNeedsPractice( + deck: deck, + questionId: 'q1', +); +``` + +### Resetting a Deck + +Reset learning progress while optionally preserving attempt history. + +```dart +// Reset all learning progress (streaks, priority, known status) +final resetDeck = DeckService.resetDeck( + deck: deck, + resetAttemptCounts: false, // Keep attempt history if true +); +``` + +### Complete Example + +```dart +import 'package:practice_engine/practice_engine.dart'; + +void main() { + // Setup + final config = const DeckConfig( + requiredConsecutiveCorrect: 2, + defaultAttemptSize: 5, + ); + + final questions = [ + Question(id: 'q1', prompt: '2+2?', answers: ['3', '4'], correctAnswerIndex: 1), + Question(id: 'q2', prompt: '3+3?', answers: ['5', '6'], correctAnswerIndex: 1), + Question(id: 'q3', prompt: '4+4?', answers: ['7', '8'], correctAnswerIndex: 1), + ]; + + var deck = Deck( + id: 'example', + title: 'Example Deck', + description: 'Simple math', + questions: questions, + config: config, + ); + + // Practice session + final service = AttemptService(); + final attempt = service.createAttempt(deck: deck); + + // Simulate user answers (all correct) + final answers = { + for (var q in attempt.questions) q.id: q.correctAnswerIndex + }; + + final result = service.processAttempt( + deck: deck, + attempt: attempt, + answers: answers, + ); + + deck = result.updatedDeck; + print('Score: ${result.result.percentageCorrect}%'); + print('Deck progress: ${deck.practicePercentage.toStringAsFixed(1)}%'); +} +``` + +## Architecture + +The package is organized into three main layers: + +- **Models** (`lib/models/`): Immutable data classes representing the domain + - `Deck`: Container for questions and configuration + - `Question`: Individual question with learning state + - `Attempt`: A practice session + - `AttemptResult`: Results from processing an attempt + - `DeckConfig`: Configuration settings + +- **Algorithms** (`lib/algorithms/`): Pure functions for calculations + - `WeightedSelector`: Selects questions based on priority and spaced repetition + - `SpacedRepetition`: Calculates probability for known questions + - `PriorityManager`: Manages priority point calculations + +- **Services** (`lib/logic/`): Business logic and orchestration + - `DeckService`: Deck-level operations (resets, manual overrides) + - `AttemptService`: Attempt creation and processing + +All models are immutable and use `copyWith` for updates, ensuring predictable state management. + +## Testing + +### Running Tests + +From the `packages/practice_engine` directory: + +```bash +# Run all tests +dart test + +# Run tests with coverage +dart test --coverage=coverage + +# Run a specific test file +dart test test/deck_test.dart + +# Run tests in watch mode (requires dart_test package) +dart test --watch +``` + +### Test Coverage + +The package includes comprehensive test coverage for: + +- Deck creation and statistics +- Question state management +- Attempt creation and processing +- Priority and spaced repetition algorithms +- Manual overrides +- Reset functionality +- Configuration validation + +Test files are located in the `test/` directory and mirror the structure of the `lib/` directory. + +## Project Structure + +``` +practice_engine/ +├── lib/ +│ ├── algorithms/ # Core algorithms (selection, repetition, priority) +│ ├── logic/ # Business logic services +│ ├── models/ # Data models +│ └── practice_engine.dart # Main export file +├── test/ # Test files +├── coverage/ # Test coverage reports +├── pubspec.yaml # Package configuration +└── README.md # This file +``` + +## Integration + +This package is designed to be used as a library. To use it in your application: + +1. **Flutter Apps**: Add to `pubspec.yaml` and import as shown in usage examples +2. **Dart CLI Tools**: Same as Flutter, just import and use +3. **Web Apps**: Compatible with Dart web compilation + +The package has no external dependencies beyond the Dart SDK, making it lightweight and easy to integrate. + +## Contributing + +When contributing, ensure all tests pass: + +```bash +dart test +``` + +Follow the existing code style and maintain test coverage for new features. -A new Flutter project.