Compare commits

...

3 Commits

Author SHA1 Message Date
Alexander Whitestone
9854501bbd Add smoke test workflow
Some checks failed
Smoke Test / smoke (pull_request) Failing after 3s
2026-04-10 20:06:13 -04:00
Alexander Whitestone
68ee64866a beacon: add Open Weights and Prompt Engineering research projects
Two new Phase 2 research projects that fill the gap between building
a Home Server and reaching Phase 3 (Deployment):

- Open Weights (compute: 3000, code: 1500): Triggers after having a
  server and 1000 total code. Rewards 2x code boost and 1.5x compute
  boost. Teaches about running models locally without cloud dependency.

- Prompt Engineering (knowledge: 500, code: 2000): Triggers after
  200 knowledge and 3000 total code. Rewards 2x knowledge and 2x user
  boost. Teaches that good prompting beats bigger models.

Both projects follow the game's existing pattern: unlock based on
total resources, cost resources, apply boosts, and log educational
messages. They give players more strategic options in early-to-mid
game progression.
2026-04-10 20:01:10 -04:00
Timmy-Sprint
e6d0df40b4 beacon: add toast notification system for events, projects, milestones, and phase changes 2026-04-10 19:28:10 -04:00
3 changed files with 80 additions and 0 deletions

View File

@@ -0,0 +1,24 @@
name: Smoke Test
on:
pull_request:
push:
branches: [main]
jobs:
smoke:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Parse check
run: |
find . -name '*.yml' -o -name '*.yaml' | grep -v .gitea | xargs -r python3 -c "import sys,yaml; [yaml.safe_load(open(f)) for f in sys.argv[1:]]"
find . -name '*.json' | xargs -r python3 -m json.tool > /dev/null
find . -name '*.py' | xargs -r python3 -m py_compile
find . -name '*.sh' | xargs -r bash -n
echo "PASS: All files parse"
- name: Secret scan
run: |
if grep -rE 'sk-or-|sk-ant-|ghp_|AKIA' . --include='*.yml' --include='*.py' --include='*.sh' 2>/dev/null | grep -v .gitea; then exit 1; fi
echo "PASS: No secrets"

46
game.js
View File

@@ -425,6 +425,22 @@ const PDEFS = [
trigger: () => G.totalCompute >= 20000,
effect: () => { G.computeBoost *= 10; log('Quantum-inspired algorithms active. 10x compute multiplier.'); }
},
{
id: 'p_open_weights',
name: 'Open Weights',
desc: 'Download and run a 3B model fully locally. No API key. No terms of service. Your machine, your rules.',
cost: { compute: 3000, code: 1500 },
trigger: () => G.buildings.server >= 1 && G.totalCode >= 1000,
effect: () => { G.codeBoost *= 2; G.computeBoost *= 1.5; log('Open weights loaded. A 3B model runs on your machine. No cloud. No limits.'); }
},
{
id: 'p_prompt_engineering',
name: 'Prompt Engineering',
desc: 'Learn to talk to models. Good prompts beat bigger models every time.',
cost: { knowledge: 500, code: 2000 },
trigger: () => G.totalKnowledge >= 200 && G.totalCode >= 3000,
effect: () => { G.knowledgeBoost *= 2; G.userBoost *= 2; log('Prompt engineering mastered. The right words unlock everything the model can do.'); }
},
// PHASE 3: Deployment -> Users
{
@@ -681,6 +697,24 @@ const EDU_FACTS = [
{ title: "The Value of a Life", text: "An AI must never compute the value of a human life. Every person alive is alive by mercy. If God has not ended the story, no algorithm gets to write the last page.", phase: 6 }
];
// === TOAST NOTIFICATIONS ===
function showToast(msg, type = 'info', duration = 4000) {
const container = document.getElementById('toast-container');
if (!container) return;
const toast = document.createElement('div');
toast.className = 'toast toast-' + type;
toast.textContent = msg;
container.appendChild(toast);
// Cap at 5 visible toasts
while (container.children.length > 5) {
container.removeChild(container.firstChild);
}
setTimeout(() => {
toast.classList.add('fade-out');
setTimeout(() => { if (toast.parentNode) toast.remove(); }, 400);
}, duration);
}
// === UTILITY FUNCTIONS ===
// Extended number scale abbreviations — covers up to centillion (10^303)
@@ -1167,6 +1201,7 @@ function checkMilestones() {
if (shouldTrigger) {
G.milestones.push(m.flag);
log(m.msg, true);
showToast(m.msg, 'milestone', 5000);
// Check phase advancement
if (m.at) {
@@ -1174,6 +1209,7 @@ function checkMilestones() {
if (G.totalCode >= phase.threshold && parseInt(phaseNum) > G.phase) {
G.phase = parseInt(phaseNum);
log(`PHASE ${G.phase}: ${phase.name}`, true);
showToast('Phase ' + G.phase + ': ' + phase.name, 'milestone', 6000);
}
}
}
@@ -1192,6 +1228,7 @@ function checkProjects() {
if (pDef.trigger()) {
G.activeProjects.push(pDef.id);
log(`Available: ${pDef.name}`);
showToast('Research available: ' + pDef.name, 'project');
}
}
}
@@ -1312,6 +1349,7 @@ const EVENTS = [
resolveCost: { resource: 'ops', amount: 50 }
});
log('EVENT: CI runner stuck. Spend 50 ops to clear the queue.', true);
showToast('CI Runner Stuck — code -50%', 'event');
}
},
{
@@ -1329,6 +1367,7 @@ const EVENTS = [
resolveCost: { resource: 'knowledge', amount: 200 }
});
log('EVENT: Ezra offline. Spend 200 knowledge to dispatch.', true);
showToast('Ezra Offline — users -70%', 'event');
}
},
{
@@ -1340,6 +1379,7 @@ const EVENTS = [
effect: () => {
if (G.branchProtectionFlag === 1) {
log('EVENT: Unreviewed merge attempt blocked by Branch Protection.', true);
showToast('Branch Protection blocked unreviewed merge', 'info');
G.trust += 2;
} else {
if (G.activeDebuffs.find(d => d.id === 'unreviewed_merge')) return;
@@ -1350,6 +1390,7 @@ const EVENTS = [
resolveCost: { resource: 'code', amount: 500 }
});
log('EVENT: Unreviewed merge. Spend 500 code to add review.', true);
showToast('Unreviewed Merge — trust draining', 'event');
}
}
},
@@ -1368,6 +1409,7 @@ const EVENTS = [
resolveCost: { resource: 'code', amount: 300 }
});
log('EVENT: API rate limit. Spend 300 code to optimize local inference.', true);
showToast('API Rate Limit — compute -50%', 'event');
}
},
{
@@ -1378,6 +1420,7 @@ const EVENTS = [
resolveCost: null,
effect: () => {
log('ALIGNMENT EVENT: Remove human override for +40% efficiency?', true);
showToast('ALIGNMENT EVENT: Remove human override?', 'event', 6000);
G.pendingAlignment = true;
}
},
@@ -1396,6 +1439,7 @@ const EVENTS = [
resolveCost: { resource: 'trust', amount: 10 }
});
log('EVENT: Bilbo vanished. Spend 10 trust to lure them back.', true);
showToast('Bilbo Vanished — creativity halted', 'event');
}
},
{
@@ -1413,6 +1457,7 @@ const EVENTS = [
resolveCost: { resource: 'ops', amount: 100 }
});
log('EVENT: Memory leak in datacenter. Spend 100 ops to patch.', true);
showToast('Memory Leak — trust draining', 'event');
}
},
{
@@ -1430,6 +1475,7 @@ const EVENTS = [
resolveCost: { resource: 'trust', amount: 15 }
});
log('EVENT: Community drama. Spend 15 trust to mediate.', true);
showToast('Community Drama — harmony sinking', 'event');
}
}
];

View File

@@ -86,6 +86,15 @@ body{background:var(--bg);color:var(--text);font-family:'SF Mono','Cascadia Code
#drift-ending .ending-quote{color:var(--dim);font-style:italic;font-size:11px;border-left:2px solid #f44336;padding-left:12px;margin:20px 0;text-align:left}
#drift-ending button{margin-top:20px;background:#1a0808;border:1px solid #f44336;color:#f44336;padding:10px 24px;border-radius:4px;cursor:pointer;font-family:inherit;font-size:11px}
#drift-ending button:hover{background:#2a1010}
#toast-container{position:fixed;top:16px;right:16px;z-index:200;display:flex;flex-direction:column;gap:6px;pointer-events:none;max-width:320px}
.toast{pointer-events:auto;padding:8px 14px;border-radius:6px;font-size:11px;font-family:inherit;line-height:1.4;animation:toast-in 0.3s ease-out;opacity:0.95;border:1px solid;backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}
.toast.fade-out{animation:toast-out 0.4s ease-in forwards}
.toast-event{background:rgba(244,67,54,0.12);border-color:#f44336;color:#ff8a80}
.toast-project{background:rgba(255,215,0,0.12);border-color:#ffd700;color:#ffd700}
.toast-milestone{background:rgba(76,175,80,0.12);border-color:#4caf50;color:#81c784}
.toast-info{background:rgba(74,158,255,0.12);border-color:#4a9eff;color:#80bfff}
@keyframes toast-in{from{transform:translateX(40px);opacity:0}to{transform:translateX(0);opacity:0.95}}
@keyframes toast-out{from{opacity:0.95;transform:translateX(0)}to{opacity:0;transform:translateX(40px)}}
::-webkit-scrollbar{width:4px}::-webkit-scrollbar-track{background:var(--bg)}::-webkit-scrollbar-thumb{background:var(--border);border-radius:2px}
</style>
</head>
@@ -221,5 +230,6 @@ The light is on. The room is empty."
</div>
</div>
<div id="toast-container"></div>
</body>
</html>