Compare commits

...

3 Commits

Author SHA1 Message Date
Timmy-Sprint
18eae67ff9 beacon: add session stats panel tracking code/buildings/combo/events/clicks
Some checks failed
Accessibility Checks / a11y-audit (pull_request) Successful in 9s
Smoke Test / smoke (pull_request) Failing after 16s
Tracks per-session metrics visible in a new SESSION section:
- Session code gained
- Buildings built this session
- Best combo streak
- Events resolved
- Clicks with rate (clicks/s)
- Session time

Also fixes totalClicks counter which was incorrectly incrementing
totalAutoClicks instead of totalClicks on manual clicks.
2026-04-20 12:16:39 -04:00
d5645fea58 Merge pull request 'fix: resolve #192 — move dead code to docs/reference, fix GENOME.md' (#194) from fix/192-dead-code-cleanup into main
Merge PR #194: fix: resolve #192 — move dead code to docs/reference, fix GENOME.md
2026-04-17 01:47:15 +00:00
Alexander Whitestone
db08f9a478 fix: resolve #192 — move dead code to docs/reference, fix GENOME.md
Some checks failed
Accessibility Checks / a11y-audit (pull_request) Successful in 9s
Smoke Test / smoke (pull_request) Failing after 16s
- game/npc-logic.js → docs/reference/npc-logic-prototype.js (ES module, never imported)
- scripts/guardrails.js → docs/reference/guardrails-prototype.js (HP/MP validation, wrong game)
- Updated GENOME.md architecture diagram to reflect actual file structure
- Updated DEAD_CODE_AUDIT to mark these as resolved
- Corrected JS line counts (6,033 across 11 files)
- Removed empty game/ directory

The actual CI scripts (guardrails.sh, smoke.mjs) remain active in scripts/.
2026-04-15 21:25:38 -04:00
8 changed files with 57 additions and 17 deletions

View File

@@ -8,24 +8,32 @@ The Beacon is a browser-based idle/incremental game inspired by Universal Paperc
Static HTML/JS — no build step, no dependencies, no framework. Open `index.html` in any browser.
**5,128 lines of JavaScript** across 10 files. **1 HTML file** with embedded CSS (~300 lines). **1 Python test file** for reckoning projects.
**6,033 lines of JavaScript** across 11 files. **1 HTML file** with embedded CSS (~300 lines). **3 test files** (2 Node.js, 1 Python).
## Architecture
```
index.html (UI + embedded CSS)
index.html (UI + embedded CSS + inline JS ~5000L)
|
+-- js/engine.js (1590L) Core game loop, tick, resources, buildings, projects, events
+-- js/data.js (944L) Building definitions, project trees, event tables, phase data
+-- js/render.js (390L) DOM rendering, UI updates, resource displays
+-- js/combat.js (359L) Boss encounters, combat mechanics
+-- js/combat.js (359L) Canvas boid-flocking combat visualization
+-- js/sound.js (401L) Web Audio API ambient drone, phase-aware sound
+-- js/dismantle.js (570L) The Dismantle sequence (late-game narrative)
+-- js/main.js (223L) Initialization, game loop start, auto-save, help overlay
+-- js/utils.js (314L) Formatting, save/load, export/import, DOM helpers
+-- js/tutorial.js (251L) New player tutorial, step-by-step guidance
+-- js/strategy.js (68L) NPC strategy logic for combat
+-- game/npc-logic.js (18L) NPC behavior stub
+-- js/emergent-mechanics.js Emergent game mechanics from player behavior
CI scripts (not browser runtime):
+-- scripts/guardrails.sh Static analysis guardrails for game logic
+-- scripts/smoke.mjs Playwright smoke tests
Reference prototypes (NOT loaded by runtime):
+-- docs/reference/npc-logic-prototype.js NPC state machine prototype
+-- docs/reference/guardrails-prototype.js Stat validation prototype
```
## Entry Points

View File

@@ -3,20 +3,15 @@ _2026-04-12, Perplexity QA_
## Findings
### Potentially Unimported Files
### Dead Code — Resolved (2026-04-15, Issue #192)
The following files were added by recent PRs but may not be imported
by the main game runtime (`js/main.js``js/engine.js`):
The following files were confirmed dead code — never imported by any runtime module.
They have been moved to `docs/reference/` as prototype reference code.
| File | Added By | Lines | Status |
|------|----------|-------|--------|
| `game/npc-logic.js` | PR #79 (GOFAI NPC State Machine) | ~150 | **Verify import** |
| `scripts/guardrails.js` | PR #80 (GOFAI Symbolic Guardrails) | ~120 | **Verify import** |
**Action:** Check if `js/main.js` or `js/engine.js` imports from `game/` or `scripts/`.
If not, these files are dead code and should either be:
1. Imported and wired into the game loop, or
2. Moved to `docs/` as reference implementations
| File | Original | Resolution |
|------|----------|------------|
| `game/npc-logic.js` | PR #79 (GOFAI NPC State Machine) | **Moved to `docs/reference/npc-logic-prototype.js`** — ES module using `export default`, incompatible with the global-script loading pattern. Concept (NPC state machine) is sound but not wired into any game system. |
| `scripts/guardrails.js` | PR #80 (GOFAI Symbolic Guardrails) | **Moved to `docs/reference/guardrails-prototype.js`** — validates HP/MP/stats concepts that don't exist in The Beacon's resource system. The `scripts/guardrails.sh` (bash CI script) remains active. |
### game.js Bloat (PR #76)

View File

@@ -205,6 +205,8 @@ Events Resolved: <span id="st-resolved">0</span><br>
<span id="emergent-stats" style="color:#b388ff;display:none">✦ Emergent Events: <span id="st-emergent">0</span> | Patterns: <span id="st-patterns">0</span> | Strategy: <span id="st-strategy"></span></span>
</div>
<div id="production-breakdown" style="display:none;margin-top:12px;padding-top:10px;border-top:1px solid var(--border)"></div>
<h2 style="margin-top:12px">SESSION</h2>
<div id="session-stats"></div>
</div>
</div>
<div id="edu-panel" role="region" aria-label="Educational Content">

View File

@@ -160,6 +160,12 @@ const G = {
startTime: 0,
flags: {},
// Session stats (reset on page reload, not persisted)
sessionCode: 0,
sessionBuildings: 0,
sessionEvents: 0,
bestCombo: 0,
// Endgame sequence
beaconEnding: false,
dismantleTriggered: false,

View File

@@ -407,6 +407,7 @@ function buyBuilding(id) {
G[resource] -= amount;
}
G.buildings[id] = (G.buildings[id] || 0) + qty;
G.sessionBuildings += qty;
updateRates();
// Emergent mechanics: track building purchase
if (typeof EmergentMechanics !== 'undefined' && window._emergent) {
@@ -781,6 +782,7 @@ function resolveEvent(debuffId) {
G[resource] -= amount;
G.activeDebuffs.splice(idx, 1);
G.totalEventsResolved = (G.totalEventsResolved || 0) + 1;
G.sessionEvents = (G.sessionEvents || 0) + 1;
log(`Resolved: ${debuff.title}. Problem fixed.`, true);
// Refund partial trust for resolution effort
G.trust += 3;
@@ -797,13 +799,15 @@ function writeCode() {
const amount = getClickPower() * comboMult;
G.code += amount;
G.totalCode += amount;
G.totalAutoClicks++;
G.sessionCode += amount;
G.totalClicks++;
// Emergent mechanics: track click
if (typeof EmergentMechanics !== 'undefined' && window._emergent) {
window._emergent.track('click', { resource: 'code', delta: amount });
}
// Combo: each consecutive click within 2s adds 0.2x multiplier, max 5x
G.comboCount++;
G.bestCombo = Math.max(G.bestCombo, G.comboCount);
G.comboTimer = G.comboDecay;
// Combo milestone bonuses: sustained clicking earns ops and knowledge
if (G.comboCount === 10) {

View File

@@ -4,6 +4,7 @@ function render() {
renderBuildings();
renderProjects();
renderStats();
renderSessionStats();
updateEducation();
renderAlignment();
renderProgress();
@@ -388,3 +389,27 @@ function loadGame() {
return false;
}
}
// === SESSION STATS ===
function renderSessionStats() {
const el = document.getElementById('session-stats');
if (!el) return;
const elapsed = Math.floor(G.playTime);
const m = Math.floor(elapsed / 60);
const s = elapsed % 60;
const timeStr = m > 0 ? `${m}m ${s}s` : `${s}s`;
const clicksPerSec = elapsed > 0 ? (G.totalClicks / elapsed).toFixed(1) : '0.0';
el.innerHTML = `
<div style="font-size:9px;color:#555;line-height:1.8">
Session Code: <span style="color:#4a9eff">${fmt(G.sessionCode || 0)}</span><br>
Buildings Built: <span style="color:#4caf50">${fmt(G.sessionBuildings || 0)}</span><br>
Best Combo: <span style="color:#ffd700">${G.bestCombo || 0}x</span><br>
Events Resolved: <span style="color:#b388ff">${G.sessionEvents || 0}</span><br>
Clicks: <span style="color:#888">${fmt(G.totalClicks || 0)} (${clicksPerSec}/s)</span><br>
Session Time: <span style="color:#888">${timeStr}</span>
</div>
`;
}