Files
chess-project/scripts/add-wikipedia-to-openings.ts
Stefan 002ed92bea Add mobile app support and opening training feature
This commit implements iOS/Android mobile app support using Capacitor
and adds a comprehensive opening training feature with LLM-powered
explanations.

## Mobile App Infrastructure

- Add Capacitor configuration for iOS/Android builds
- Create mobile build script that excludes API routes
- Update Next.js config for conditional static export
- Add layout components with generateStaticParams for static builds
- Generate 500+ static pages for offline mobile use

## Chess Engine Abstraction

- Create ChessEngine interface for pluggable implementations
- Add LocalEngine (GPL - uses stockfish.js in browser)
- Add RemoteEngine (proprietary - calls API server)
- Factory pattern selects engine based on environment
- Enables GPL compliance for web, proprietary for mobile

## Opening Training Feature

- Interactive opening repertoire training
- Move validation with engine-backed feedback
- LLM explanations using Gemini API
- Wikipedia integration for opening context
- Opening family grouping (e4, d4, c4, etc.)
- Session state management
- Real-time move feedback with evaluation

Components:
- OpeningSelector: Browse and select openings by family
- OpeningTrainer: Main training interface with chessboard
- MoveFeedback: Display move quality and LLM explanations
- WikipediaSummary: Show opening history and context
- ErrorBoundary: Graceful error handling

Services:
- openingLoader: Load and filter opening database
- engineService: Engine evaluation wrapper
- moveValidator: Validate moves against repertoire
- feedbackGenerator: Generate contextual feedback
- wikipediaService: Fetch and cache Wikipedia data
- sessionManager: Track training session state

## Wikipedia Integration

- Automatic Wikipedia article fetching for openings
- Client-side and server-side caching
- Sanitized summaries with proper formatting
- Link opening database to Wikipedia slugs
- API endpoints for on-demand fetching

## Docker Improvements

- Add entrypoint script for automatic data setup
- Fetch Wikipedia data on first container startup
- Generate opening move index automatically
- Remove generated data from git (public/openings/*.json, public/wikipedia/*.json)
- Add READMEs explaining data requirements
- Update .gitignore for generated files

## Dual Licensing Strategy

- Add LICENSING.md explaining dual licensing approach
- GPL-3.0 for web builds (includes Stockfish)
- Proprietary option for mobile builds (no GPL code)
- Single codebase, multiple licensing models
- Legal compliance documented

## API Endpoints

- POST /api/v1/llm/opening-explanation - Get LLM move explanations
- GET /api/v1/wikipedia/summary - Fetch Wikipedia summaries

## Type Updates

- Add openingTraining types
- Update Tutor component to use ChessEngine interface
- Add Gemini error handling types

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-07 18:40:58 +01:00

128 lines
3.2 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env tsx
/**
* Add Wikipedia slugs to opening database
*
* This script updates the opening JSON files to include wikipediaSlug field
* based on the cached Wikipedia articles we've already downloaded.
*/
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
interface OpeningMetadata {
name: string;
eco: string;
moves: string;
isEcoRoot?: boolean;
wikipediaSlug?: string;
}
/**
* Extract family name from opening name
*/
function extractFamilyName(openingName: string): string {
const separators = [':', ',', '', '—', ' - '];
for (const sep of separators) {
if (openingName.includes(sep)) {
return openingName.split(sep)[0].trim();
}
}
return openingName;
}
/**
* Convert family name to slug
*/
function familyNameToSlug(familyName: string): string {
return familyName.toLowerCase().replace(/[^a-z0-9]+/g, '-');
}
/**
* Load Wikipedia cache index to see what we have
*/
function getAvailableWikipediaSlugs(): Set<string> {
const wikiDir = path.join(__dirname, '..', 'public', 'wikipedia');
const slugs = new Set<string>();
if (!fs.existsSync(wikiDir)) {
return slugs;
}
const files = fs.readdirSync(wikiDir);
for (const file of files) {
if (file.endsWith('.json') && file !== 'index.json') {
const slug = file.replace('.json', '');
slugs.add(slug);
}
}
return slugs;
}
/**
* Update opening database files with Wikipedia slugs
*/
function updateOpeningDatabases() {
const availableSlugs = getAvailableWikipediaSlugs();
console.log(`\n📚 Found ${availableSlugs.size} Wikipedia cache files\n`);
const ecoFiles = ['ecoA', 'ecoB', 'ecoC', 'ecoD', 'ecoE'];
let totalUpdated = 0;
let totalSkipped = 0;
for (const ecoFile of ecoFiles) {
const filePath = path.join(__dirname, '..', 'public', 'openings', `${ecoFile}.json`);
console.log(`\n📖 Processing ${ecoFile}.json...`);
if (!fs.existsSync(filePath)) {
console.log(` ⚠️ File not found, skipping`);
continue;
}
const data: Record<string, OpeningMetadata> = JSON.parse(
fs.readFileSync(filePath, 'utf-8')
);
let updatedCount = 0;
let skippedCount = 0;
// Update each opening
for (const [fen, opening] of Object.entries(data)) {
const familyName = extractFamilyName(opening.name);
const slug = familyNameToSlug(familyName);
if (availableSlugs.has(slug)) {
opening.wikipediaSlug = slug;
updatedCount++;
} else {
skippedCount++;
}
}
// Write updated file
fs.writeFileSync(filePath, JSON.stringify(data, null, 2));
console.log(` ✓ Updated ${updatedCount} openings`);
console.log(` ⚠️ Skipped ${skippedCount} (no Wikipedia cache)`);
totalUpdated += updatedCount;
totalSkipped += skippedCount;
}
console.log('\n' + '='.repeat(50));
console.log('✨ Wikipedia Slug Addition Complete!\n');
console.log(`✓ Total updated: ${totalUpdated}`);
console.log(`⚠️ Total skipped: ${totalSkipped}`);
console.log('='.repeat(50) + '\n');
}
// Run the script
console.log('🔗 Adding Wikipedia Slugs to Opening Database\n');
updateOpeningDatabases();