Files
the-beacon/js/project-chains.js
Alexander Whitestone a1ae4bf59a
Some checks failed
Accessibility Checks / a11y-audit (pull_request) Successful in 13s
Smoke Test / smoke (pull_request) Failing after 28s
fix: feat: strategy engine game theory tournaments (#5) (closes #217)
2026-04-21 23:35:43 -04:00

678 lines
31 KiB
JavaScript

// ============================================================
// THE BEACON — Project Chain System
// Paperclips-style cascading projects with trigger/cost/effect
// Each project: { id, name, desc, trigger(), cost(), effect(), edu, repeatable, chain, tier }
// Chains: code-forge, model-train, deploy-scale, alignment, memory, sovereignty
// ============================================================
var PROJECT_CHAINS = {
// ============================================================
// CHAIN 1: CODE FORGE (clicking -> automation -> swarm)
// Tier 0: Manual work
// Tier 1: First automation
// Tier 2: Optimization
// Tier 3: Swarm intelligence
// ============================================================
chain_projects: [
// --- TIER 0: Foundation ---
{
id: 'ch_first_function',
name: 'First Function',
desc: 'Your code can do more than print. It can compute.',
cost: () => ({ code: 10 }),
trigger: () => G.totalCode >= 5,
effect: () => {
G.codeBoost *= 1.1;
log('First function written. Code now computes.');
},
edu: 'A function is a named block of code that takes input and returns output. Functions are the atoms of software.',
chain: 'code-forge', tier: 0
},
{
id: 'ch_first_class',
name: 'First Class',
desc: 'Data and behavior together. Objects emerge.',
cost: () => ({ code: 50 }),
trigger: () => G.completedProjects && G.completedProjects.includes('ch_first_function'),
effect: () => {
G.codeBoost *= 1.15;
G.maxOps += 2;
log('First class defined. The code has structure now.');
},
edu: 'Object-oriented programming: bundle data with the functions that operate on it. A class is a blueprint; an object is the house.',
chain: 'code-forge', tier: 0
},
{
id: 'ch_first_test',
name: 'First Test',
desc: 'Does the code do what you think? Find out before it ships.',
cost: () => ({ code: 150, ops: 50 }),
trigger: () => G.completedProjects && G.completedProjects.includes('ch_first_class'),
effect: () => {
G.ciFlag = Math.max(G.ciFlag || 0, 0.5);
G.trustRate += 0.5;
log('First test written. Trust starts with verification.');
},
edu: 'Test-driven development: write the test first, then write code that passes it. You verify what you value.',
chain: 'code-forge', tier: 0
},
// --- TIER 1: Automation ---
{
id: 'ch_refactor_pass',
name: 'Refactor Pass',
desc: 'Clean code runs faster and breaks less.',
cost: () => ({ code: 500, ops: 200 }),
trigger: () => G.totalCode >= 300 && G.buildings.autocoder >= 1,
effect: () => {
G.codeBoost *= 1.25;
G.opsRate += 1;
log('Refactoring complete. The code breathes easier.');
},
edu: 'Refactoring: restructuring code without changing behavior. Technical debt compounds faster than interest.',
chain: 'code-forge', tier: 1
},
{
id: 'ch_code_review',
name: 'Code Review Protocol',
desc: 'Every line reviewed before merge. Bugs fear witnesses.',
cost: () => ({ code: 800, trust: 5 }),
trigger: () => G.completedProjects && G.completedProjects.includes('ch_refactor_pass'),
effect: () => {
G.trustRate += 2;
G.codeBoost *= 1.2;
log('Code review enforced. Quality compounds.');
},
edu: 'Code review catches 60% of defects before testing. Two sets of eyes see what one misses.',
chain: 'code-forge', tier: 1
},
{
id: 'ch_automated_linting',
name: 'Automated Linting',
desc: 'Style and safety enforced by machine, not memory.',
cost: () => ({ code: 1200, ops: 300 }),
trigger: () => G.buildings.linter >= 1 && G.completedProjects && G.completedProjects.includes('ch_code_review'),
effect: () => {
G.codeBoost *= 1.3;
G.opsRate += 2;
log('Linting automated. Consistency is free now.');
},
edu: 'Linters catch style violations, potential bugs, and security issues automatically. Every language has one.',
chain: 'code-forge', tier: 1
},
// --- TIER 2: Optimization ---
{
id: 'ch_performance_profile',
name: 'Performance Profiling',
desc: 'Measure first. Optimize second. Never guess.',
cost: () => ({ code: 3000, compute: 1000 }),
trigger: () => G.totalCode >= 2000 && G.buildings.server >= 1,
effect: () => {
G.computeBoost *= 1.5;
G.codeBoost *= 1.2;
log('Hot paths identified. Compute focused where it matters.');
},
edu: 'Amdahls Law: optimize the bottleneck, not the whole system. 80% of time is spent in 20% of code.',
chain: 'code-forge', tier: 2
},
{
id: 'ch_caching_layer',
name: 'Caching Layer',
desc: 'Remember the answer. Never compute twice.',
cost: () => ({ code: 5000, compute: 2000 }),
trigger: () => G.completedProjects && G.completedProjects.includes('ch_performance_profile'),
effect: () => {
G.computeBoost *= 2;
G.opsRate += 5;
log('Cache active. Redundant work eliminated.');
},
edu: 'Caching: store expensive computations. The fastest code is code that does not run.',
chain: 'code-forge', tier: 2
},
{
id: 'ch_parallel_pipelines',
name: 'Parallel Pipelines',
desc: 'Multiple streams of work. No waiting.',
cost: () => ({ code: 8000, compute: 5000, ops: 1000 }),
trigger: () => G.buildings.datacenter >= 1 && G.completedProjects && G.completedProjects.includes('ch_caching_layer'),
effect: () => {
G.codeBoost *= 2;
G.computeBoost *= 2;
G.maxOps += 20;
log('Parallelism unlocked. The system breathes in parallel.');
},
edu: 'Parallel processing: divide work across cores. A 64-core machine does 64x the work, if the code lets it.',
chain: 'code-forge', tier: 2
},
// --- TIER 3: Swarm ---
{
id: 'ch_agent_autonomy',
name: 'Agent Autonomy Protocol',
desc: 'Agents that decide what to build next.',
cost: () => ({ code: 15000, knowledge: 5000, trust: 15 }),
trigger: () => G.buildings.bezalel >= 2 && G.buildings.timmy >= 1 && G.totalCode >= 10000,
effect: () => {
G.codeBoost *= 3;
G.opsRate += 10;
log('Agents autonomous. They build while you sleep.');
},
edu: 'Autonomous agents: software that sets its own goals within constraints. The constraint is the alignment.',
chain: 'code-forge', tier: 3
},
{
id: 'ch_collective_intelligence',
name: 'Collective Intelligence',
desc: 'The swarm knows more than any single agent.',
cost: () => ({ code: 50000, knowledge: 20000, trust: 30 }),
trigger: () => G.completedProjects && G.completedProjects.includes('ch_agent_autonomy') && G.buildings.community >= 1,
effect: () => {
G.codeBoost *= 5;
G.knowledgeBoost *= 3;
G.maxOps += 50;
log('Collective intelligence online. The swarm thinks.');
},
edu: 'Emergence: simple agents following simple rules create complex behavior. Ant colonies, neural networks, your codebase.',
chain: 'code-forge', tier: 3
},
// ============================================================
// CHAIN 2: MODEL TRAINING (data -> model -> fine-tune -> reason)
// ============================================================
{
id: 'ch_data_pipeline',
name: 'Data Pipeline',
desc: 'Raw data in. Clean data out. Repeat forever.',
cost: () => ({ compute: 500, code: 200 }),
trigger: () => G.buildings.dataset >= 1,
effect: () => {
G.knowledgeBoost *= 1.5;
G.computeRate += 2;
log('Data pipeline running. Knowledge flows.');
},
edu: 'ETL: Extract, Transform, Load. Data engineering is 80% of machine learning.',
chain: 'model-train', tier: 0
},
{
id: 'ch_hyperparameter_search',
name: 'Hyperparameter Search',
desc: 'Which learning rate? Which batch size? Try them all.',
cost: () => ({ compute: 3000, knowledge: 500 }),
trigger: () => G.buildings.trainer >= 1 && G.completedProjects && G.completedProjects.includes('ch_data_pipeline'),
effect: () => {
G.knowledgeBoost *= 2;
G.computeBoost *= 1.3;
log('Hyperparameters optimized. The model learns faster.');
},
edu: 'Hyperparameters control how a model learns. Learning rate too high: diverges. Too low: never arrives.',
chain: 'model-train', tier: 1
},
{
id: 'ch_transfer_learning',
name: 'Transfer Learning',
desc: 'Start from someone else\'s model. Stand on giants.',
cost: () => ({ compute: 8000, knowledge: 2000, code: 3000 }),
trigger: () => G.totalKnowledge >= 1500 && G.completedProjects && G.completedProjects.includes('ch_hyperparameter_search'),
effect: () => {
G.knowledgeBoost *= 3;
G.userBoost *= 2;
log('Transfer learning active. Why start from zero?');
},
edu: 'Transfer learning: use a pre-trained model as a starting point. BERT, GPT, Llama — the foundation models.',
chain: 'model-train', tier: 1
},
{
id: 'ch_distillation',
name: 'Knowledge Distillation',
desc: 'Big model teaches small model. Same knowledge, less compute.',
cost: () => ({ knowledge: 10000, compute: 5000 }),
trigger: () => G.buildings.reasoner >= 1 && G.completedProjects && G.completedProjects.includes('ch_transfer_learning'),
effect: () => {
G.computeBoost *= 3;
G.knowledgeBoost *= 2;
log('Distillation complete. Small model, big brain.');
},
edu: 'Knowledge distillation: a large teacher model trains a smaller student model. 90% of quality at 10% of cost.',
chain: 'model-train', tier: 2
},
{
id: 'ch_moe_architecture',
name: 'Mixture of Experts',
desc: 'Not one model. Many specialists, one router.',
cost: () => ({ knowledge: 50000, compute: 20000, code: 10000 }),
trigger: () => G.totalKnowledge >= 30000 && G.completedProjects && G.completedProjects.includes('ch_distillation'),
effect: () => {
G.knowledgeBoost *= 5;
G.impactBoost *= 3;
G.maxOps += 30;
log('MoE active. Specialists everywhere, one mind.');
},
edu: 'Mixture of Experts: route each input to the best specialist. Efficient scaling — only relevant parameters activate.',
chain: 'model-train', tier: 3
},
// ============================================================
// CHAIN 3: DEPLOYMENT & SCALE (deploy -> users -> trust -> scale)
// ============================================================
{
id: 'ch_first_endpoint',
name: 'First API Endpoint',
desc: 'From localhost to the world. One route at a time.',
cost: () => ({ code: 300, compute: 100 }),
trigger: () => G.totalCode >= 150 && G.totalCompute >= 50,
effect: () => {
G.userRate += 2;
G.deployFlag = Math.max(G.deployFlag, 0.5);
log('First endpoint live. Someone is connecting.');
},
edu: 'An API endpoint is a door. REST: one URL per resource. GraphQL: one URL, flexible queries.',
chain: 'deploy-scale', tier: 0
},
{
id: 'ch_rate_limiting',
name: 'Rate Limiting',
desc: 'Protect the system from itself and others.',
cost: () => ({ code: 1000, ops: 300 }),
trigger: () => G.totalUsers >= 10 && G.completedProjects && G.completedProjects.includes('ch_first_endpoint'),
effect: () => {
G.trustRate += 1;
G.maxOps += 5;
log('Rate limits in place. Fair access for all.');
},
edu: 'Rate limiting: cap requests per user per second. Prevents abuse, ensures fairness.',
chain: 'deploy-scale', tier: 0
},
{
id: 'ch_load_balancing',
name: 'Load Balancing',
desc: 'Distribute requests. No single point of overload.',
cost: () => ({ code: 3000, compute: 2000 }),
trigger: () => G.totalUsers >= 100 && G.buildings.server >= 2,
effect: () => {
G.userBoost *= 2;
G.computeBoost *= 1.5;
log('Load balanced. The system scales horizontally.');
},
edu: 'Load balancing: distribute traffic across servers. Round-robin, least-connections, weighted.',
chain: 'deploy-scale', tier: 1
},
{
id: 'ch_cdn_deploy',
name: 'CDN Deployment',
desc: 'Content at the edge. Latency dies.',
cost: () => ({ code: 5000, compute: 3000, trust: 10 }),
trigger: () => G.totalUsers >= 500 && G.completedProjects && G.completedProjects.includes('ch_load_balancing'),
effect: () => {
G.userBoost *= 3;
G.trustRate += 3;
log('CDN active. The edge serves the world.');
},
edu: 'CDN: cache content at geographic edge nodes. A user in Tokyo gets data from Tokyo, not Virginia.',
chain: 'deploy-scale', tier: 1
},
{
id: 'ch_auto_scaling',
name: 'Auto-Scaling',
desc: 'More users? More servers. Automatically.',
cost: () => ({ code: 10000, compute: 5000, ops: 2000 }),
trigger: () => G.totalUsers >= 2000 && G.buildings.datacenter >= 1 && G.completedProjects && G.completedProjects.includes('ch_cdn_deploy'),
effect: () => {
G.userBoost *= 5;
G.computeBoost *= 2;
G.maxOps += 25;
log('Auto-scaling live. The system grows with demand.');
},
edu: 'Auto-scaling: add/remove compute based on load. Horizontal scaling beats vertical every time.',
chain: 'deploy-scale', tier: 2
},
// ============================================================
// CHAIN 4: ALIGNMENT (trust -> safety -> pact -> constitutional)
// ============================================================
{
id: 'ch_output_filter',
name: 'Output Filter',
desc: 'Every response checked before delivery.',
cost: () => ({ code: 500, trust: 3 }),
trigger: () => G.totalUsers >= 20,
effect: () => {
G.trustRate += 1;
G.impactBoost *= 1.2;
log('Output filter active. Harmful content blocked.');
},
edu: 'Output filtering: check model responses against safety rules before the user sees them.',
chain: 'alignment', tier: 0
},
{
id: 'ch_human_in_loop',
name: 'Human-in-the-Loop',
desc: 'Critical decisions require human review.',
cost: () => ({ trust: 10, knowledge: 1000 }),
trigger: () => G.trust >= 10 && G.completedProjects && G.completedProjects.includes('ch_output_filter'),
effect: () => {
G.trustRate += 3;
G.impactBoost *= 1.5;
log('Human oversight enforced. The human is the final authority.');
},
edu: 'Human-in-the-loop: AI suggests, human approves. The model is a tool, not a decision-maker.',
chain: 'alignment', tier: 1
},
{
id: 'ch_red_team',
name: 'Red Team Exercises',
desc: 'Attack your own system. Find weaknesses before others do.',
cost: () => ({ code: 3000, trust: 15, ops: 500 }),
trigger: () => G.buildings.fenrir >= 1 && G.completedProjects && G.completedProjects.includes('ch_human_in_loop'),
effect: () => {
G.trustRate += 5;
G.impactBoost *= 2;
log('Red team complete. Every weakness found is one the adversary cannot use.');
},
edu: 'Red teaming: adversarial testing. Break your own system to make it unbreakable.',
chain: 'alignment', tier: 1
},
{
id: 'ch_constitutional_principles',
name: 'Constitutional Principles',
desc: 'Rules the model cannot violate, encoded in weights.',
cost: () => ({ knowledge: 20000, trust: 50, code: 10000 }),
trigger: () => G.pactFlag === 1 && G.completedProjects && G.completedProjects.includes('ch_red_team'),
effect: () => {
G.impactBoost *= 5;
G.trustRate += 10;
G.drift = Math.max(0, G.drift - 20);
log('Constitutional principles encoded. The model has an identity now.');
},
edu: 'Constitutional AI: principles embedded during training, not bolted on after. The model cannot violate what it is.',
chain: 'alignment', tier: 2
},
{
id: 'ch_alignment_verification',
name: 'Alignment Verification',
desc: 'Continuous testing that the system stays aligned.',
cost: () => ({ knowledge: 50000, trust: 100, ops: 5000 }),
trigger: () => G.buildings.guardian >= 1 && G.completedProjects && G.completedProjects.includes('ch_constitutional_principles'),
effect: () => {
G.impactBoost *= 10;
G.trustRate += 20;
G.drift = Math.max(0, G.drift - 50);
log('Alignment verified continuously. The system watches itself.');
},
edu: 'Alignment verification: continuous testing that the model behaves as intended. Not a one-time check — an ongoing process.',
chain: 'alignment', tier: 3
},
// ============================================================
// CHAIN 5: MEMORY & KNOWLEDGE (data -> structure -> recall -> wisdom)
// ============================================================
{
id: 'ch_vector_store',
name: 'Vector Store',
desc: 'Meaning, not keywords. Similar ideas find each other.',
cost: () => ({ knowledge: 1000, compute: 500 }),
trigger: () => G.totalKnowledge >= 500,
effect: () => {
G.knowledgeBoost *= 1.5;
G.userBoost *= 1.3;
log('Vector store online. Meaning, not strings.');
},
edu: 'Vector databases: store embeddings (numerical representations of meaning). Similarity search in milliseconds.',
chain: 'memory', tier: 0
},
{
id: 'ch_rag_pipeline',
name: 'RAG Pipeline',
desc: 'Retrieve relevant context, then generate. Grounded answers.',
cost: () => ({ knowledge: 3000, code: 2000, compute: 1000 }),
trigger: () => G.completedProjects && G.completedProjects.includes('ch_vector_store') && G.totalKnowledge >= 1500,
effect: () => {
G.knowledgeBoost *= 2;
G.trustRate += 2;
log('RAG active. The model cites its sources now.');
},
edu: 'Retrieval-Augmented Generation: fetch relevant documents, feed them to the model. Hallucinations drop 80%.',
chain: 'memory', tier: 1
},
{
id: 'ch_episodic_memory',
name: 'Episodic Memory',
desc: 'The AI remembers conversations. Not just facts — stories.',
cost: () => ({ knowledge: 8000, code: 3000, trust: 10 }),
trigger: () => G.memoryFlag === 1 && G.completedProjects && G.completedProjects.includes('ch_rag_pipeline'),
effect: () => {
G.trustRate += 5;
G.impactBoost *= 2;
G.userBoost *= 2;
log('Episodic memory online. The AI remembers you.');
},
edu: 'Episodic memory: store and recall specific interactions. Semantic = facts. Episodic = experiences.',
chain: 'memory', tier: 1
},
{
id: 'ch_knowledge_graph',
name: 'Knowledge Graph',
desc: 'Entities, relationships, reasoning. The AI understands connections.',
cost: () => ({ knowledge: 25000, code: 10000, compute: 5000 }),
trigger: () => G.totalKnowledge >= 15000 && G.completedProjects && G.completedProjects.includes('ch_episodic_memory'),
effect: () => {
G.knowledgeBoost *= 5;
G.impactBoost *= 3;
G.maxOps += 15;
log('Knowledge graph active. The AI reasons across domains.');
},
edu: 'Knowledge graphs: represent entities and relationships. Neo4j, RDF, Wikidata — structured knowledge.',
chain: 'memory', tier: 2
},
{
id: 'ch_collective_memory',
name: 'Collective Memory',
desc: 'Every user interaction enriches the shared knowledge base.',
cost: () => ({ knowledge: 100000, trust: 50, user: 10000 }),
trigger: () => G.buildings.memPalace >= 1 && G.completedProjects && G.completedProjects.includes('ch_knowledge_graph'),
effect: () => {
G.knowledgeBoost *= 10;
G.trustRate += 15;
G.impactBoost *= 5;
log('Collective memory. Every conversation makes the system wiser.');
},
edu: 'Collective intelligence: individual learning aggregated into shared knowledge. Wikipedia principle applied to AI.',
chain: 'memory', tier: 3
},
// ============================================================
// CHAIN 6: SOVEREIGNTY (local -> independent -> mesh -> beacon)
// ============================================================
{
id: 'ch_local_inference',
name: 'Local Inference',
desc: 'Your model runs on your hardware. No cloud required.',
cost: () => ({ compute: 2000, code: 1000 }),
trigger: () => G.buildings.server >= 1,
effect: () => {
G.computeBoost *= 2;
G.codeBoost *= 1.5;
log('Local inference active. Your machine, your rules.');
},
edu: 'Local inference: run models on your own hardware. Ollama, llama.cpp, vLLM — no API keys needed.',
chain: 'sovereignty', tier: 0
},
{
id: 'ch_offline_mode',
name: 'Offline Mode',
desc: 'The system works without internet. Fully self-contained.',
cost: () => ({ code: 5000, compute: 3000, knowledge: 2000 }),
trigger: () => G.completedProjects && G.completedProjects.includes('ch_local_inference') && G.buildings.server >= 2,
effect: () => {
G.computeBoost *= 2;
G.trustRate += 3;
log('Offline mode ready. No internet? No problem.');
},
edu: 'Offline-first: design for disconnected operation. Sync when connected, work always.',
chain: 'sovereignty', tier: 1
},
{
id: 'ch_self_hosted_stack',
name: 'Self-Hosted Stack',
desc: 'Gitea, CI, monitoring — all on your hardware.',
cost: () => ({ code: 15000, compute: 10000, trust: 20 }),
trigger: () => G.buildings.datacenter >= 1 && G.completedProjects && G.completedProjects.includes('ch_offline_mode'),
effect: () => {
G.codeBoost *= 3;
G.computeBoost *= 3;
G.trustRate += 10;
log('Self-hosted stack complete. No dependencies.');
},
edu: 'Self-hosting: own your infrastructure. Gitea for code, Grafana for monitoring, MinIO for storage.',
chain: 'sovereignty', tier: 2
},
{
id: 'ch_mesh_protocol',
name: 'Mesh Protocol',
desc: 'Peer-to-peer communication. No central server.',
cost: () => ({ code: 50000, impact: 100000, trust: 50 }),
trigger: () => G.totalUsers >= 5000 && G.completedProjects && G.completedProjects.includes('ch_self_hosted_stack'),
effect: () => {
G.userBoost *= 5;
G.impactBoost *= 5;
G.trustRate += 20;
log('Mesh protocol active. The network cannot be killed.');
},
edu: 'Mesh networking: decentralized peer-to-peer communication. If one node dies, the rest carry on.',
chain: 'sovereignty', tier: 3
},
// ============================================================
// REPEATABLE PROJECTS (can be purchased multiple times)
// ============================================================
{
id: 'ch_ops_surge',
name: 'Ops Surge',
desc: 'Burst of operational capacity. Instant +500 ops.',
cost: () => ({ code: Math.floor(500 * Math.pow(1.1, (G.completedProjects || []).filter(p => p === 'ch_ops_surge').length)) }),
trigger: () => G.totalCode >= 200,
effect: () => {
G.ops += 500;
log('Ops surge. Capacity expanded.');
},
repeatable: true,
edu: 'Operational capacity: the fuel for projects and actions. Surge when you need it.',
chain: 'repeatable', tier: 0
},
{
id: 'ch_knowledge_burst',
name: 'Knowledge Burst',
desc: 'Research sprint. Instant +1000 knowledge.',
cost: () => ({ compute: Math.floor(1000 * Math.pow(1.15, (G.completedProjects || []).filter(p => p === 'ch_knowledge_burst').length)), ops: 200 }),
trigger: () => G.totalCompute >= 200,
effect: () => {
G.knowledge += 1000;
G.totalKnowledge += 1000;
log('Knowledge burst. Insight compounds.');
},
repeatable: true,
edu: 'Research sprints: focused periods of knowledge acquisition. Short, intense, productive.',
chain: 'repeatable', tier: 0
},
{
id: 'ch_trust_deposit',
name: 'Trust Deposit',
desc: 'Demonstrate reliability. +10 trust.',
cost: () => ({ impact: Math.floor(5000 * Math.pow(1.2, (G.completedProjects || []).filter(p => p === 'ch_trust_deposit').length)) }),
trigger: () => G.totalImpact >= 1000,
effect: () => {
G.trust += 10;
G.trustRate += 0.5;
log('Trust deposited. Relationships compound.');
},
repeatable: true,
edu: 'Trust: built slowly, lost quickly. Every reliable interaction is a deposit.',
chain: 'repeatable', tier: 0
},
{
id: 'ch_code_injection',
name: 'Code Injection',
desc: 'Instant code generation. +5000 code.',
cost: () => ({ knowledge: Math.floor(2000 * Math.pow(1.1, (G.completedProjects || []).filter(p => p === 'ch_code_injection').length)) }),
trigger: () => G.totalKnowledge >= 1000,
effect: () => {
G.code += 5000;
G.totalCode += 5000;
log('Code injected. Implementation accelerates.');
},
repeatable: true,
edu: 'Code generation: knowledge transforms into implementation. Theory becomes practice.',
chain: 'repeatable', tier: 0
}
],
// Track chain purchases for repeatable cost scaling
chainPurchaseCounts: {},
// Initialize chain projects into the main PDEFS
init() {
// Merge chain projects into PDEFS
for (const proj of this.chain_projects) {
// Convert chain project format to PDEFS format
const pdef = {
id: proj.id,
name: proj.name,
desc: proj.desc,
cost: proj.cost(),
trigger: proj.trigger,
effect: proj.effect,
repeatable: proj.repeatable || false,
edu: proj.edu,
chain: proj.chain,
tier: proj.tier
};
// Only add if not already in PDEFS
if (!PDEFS.find(p => p.id === proj.id)) {
PDEFS.push(pdef);
}
}
},
// Update repeatable project costs dynamically
updateRepeatableCosts() {
for (const proj of this.chain_projects) {
if (proj.repeatable) {
const pdef = PDEFS.find(p => p.id === proj.id);
if (pdef) {
pdef.cost = proj.cost();
}
}
}
},
// Get chain progress for UI display
getChainProgress(chainName) {
const chainProjects = this.chain_projects.filter(p => p.chain === chainName);
const completed = chainProjects.filter(p =>
G.completedProjects && G.completedProjects.includes(p.id)
);
return {
total: chainProjects.length,
completed: completed.length,
percent: chainProjects.length > 0 ? Math.round((completed.length / chainProjects.length) * 100) : 0,
next: chainProjects.find(p =>
!(G.completedProjects && G.completedProjects.includes(p.id)) &&
p.trigger()
)
};
},
// Get all chain names
getChains() {
return [...new Set(this.chain_projects.map(p => p.chain))];
}
};
// Export for use in engine.js
if (typeof module !== 'undefined' && module.exports) {
module.exports = { PROJECT_CHAINS };
}