Last active
May 15, 2025 21:47
-
-
Save BiatuAutMiahn/360b19298bd74bb848229cedbaeaf77c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>DigitalRootCompass</title> | |
| <style> | |
| body { | |
| font-family: sans-serif; | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| padding: 20px; | |
| background-color: #f0f0f0; | |
| } | |
| #graph-container { | |
| display: flex; | |
| align-items: flex-end; /* Align bars at the bottom */ | |
| border: 1px solid #ccc; | |
| padding: 10px; | |
| height: 300px; /* Max height for graph area */ | |
| background-color: white; | |
| box-shadow: 0 0 10px rgba(0,0,0,0.1); | |
| margin-bottom: 20px; | |
| } | |
| .column { | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| margin: 0 5px; | |
| width: 50px; | |
| } | |
| .bar { | |
| width: 100%; | |
| background-color: steelblue; | |
| transition: height 0.1s linear; /* Smooth height transition */ | |
| height: 0; /* Initial height */ | |
| } | |
| .label, .count { | |
| margin-top: 5px; | |
| font-size: 12px; | |
| } | |
| .count { | |
| font-weight: bold; | |
| color: #333; | |
| } | |
| .controls button { | |
| padding: 10px 15px; | |
| margin: 5px; | |
| cursor: pointer; | |
| border: 1px solid #ccc; | |
| background-color: #e9e9e9; | |
| } | |
| .controls button:hover { | |
| background-color: #dcdcdc; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <h1>Digital Root Compass</h1> | |
| <p>Your flything through time, which path are you taking? You are a Kite with a string in a hurricane...good luck.</p> | |
| <div id="graph-container"> | |
| <!-- Columns will be generated by JavaScript --> | |
| </div> | |
| <div class="controls"> | |
| <button id="startButton">Start</button> | |
| <button id="stopButton">Stop</button> | |
| </div> | |
| <script> | |
| const dataStore = {}; | |
| const graphContainer = document.getElementById('graph-container'); | |
| const MAX_BAR_HEIGHT_PX = 275; // Max height for individual bars within the container | |
| let generatorIntervalId = null; | |
| let discarderIntervalId = null; | |
| let graphUpdateIntervalId = null; | |
| // --- Configuration --- | |
| const GENERATION_INTERVAL_MS = 9; // Add a new number every 50ms | |
| const DISCARD_INTERVAL_MS = 72; // Attempt to discard from each array every 200ms | |
| const GRAPH_UPDATE_INTERVAL_MS = 36; // Update graph display every 100ms (10 FPS) | |
| const TARGET_MAX_RANDOM_VALUE = 4294967292;//65529; // Our new "balanced" upper bound | |
| // Helper function for cryptographically secure random integer in a range [0, maxInclusive] | |
| function getSecureRandomInt(maxInclusive) { | |
| const range = maxInclusive + 1; | |
| // We'll use a Uint32Array as our source of randomness. Max value is 2^32 - 1. | |
| const maxSourceValue = 0xFFFFFFFF; | |
| // The largest multiple of 'range' that is less than or equal to (maxSourceValue + 1) | |
| // This is the 'limit' for rejection sampling to avoid modulo bias. | |
| const limit = Math.floor((maxSourceValue + 1) / range) * range; | |
| let randomNumber; | |
| const buffer = new Uint32Array(1); | |
| do { | |
| window.crypto.getRandomValues(buffer); | |
| randomNumber = buffer[0]; | |
| } while (randomNumber >= limit); // Keep generating until we get a number within the unbiased part of the source range | |
| return randomNumber % range; // Now, modulo is unbiased | |
| } | |
| 131070 | |
| function generateAndAppend() { | |
| let randomNumber; | |
| if (window.crypto && window.crypto.getRandomValues) { | |
| randomNumber = getSecureRandomInt(TARGET_MAX_RANDOM_VALUE); // Generate 0 to 65537 | |
| } else { | |
| console.error("Cryptographically secure random number generator (window.crypto.getRandomValues) is not available. Stopping number generation."); | |
| if (generatorIntervalId) { | |
| clearInterval(generatorIntervalId); | |
| generatorIntervalId = null; | |
| } | |
| return; | |
| } | |
| const root = getDigitalRoot(randomNumber); | |
| const key = root.toString(); | |
| if (dataStore[key]) { | |
| dataStore[key].push(randomNumber); | |
| } else { | |
| console.warn(`Calculated root ${root} for ${randomNumber} (crypto) has no corresponding array.`); | |
| } | |
| } | |
| // 1. Initialize Data Store and Graph Structure | |
| function initialize() { | |
| graphContainer.innerHTML = ''; // Clear previous graph if any | |
| for (let i = 1; i <= 9; i++) { | |
| const key = i.toString(); | |
| dataStore[key] = []; | |
| const columnDiv = document.createElement('div'); | |
| columnDiv.classList.add('column'); | |
| const countSpan = document.createElement('span'); | |
| countSpan.classList.add('count'); | |
| countSpan.id = `count-${key}`; | |
| countSpan.textContent = '0'; | |
| const barDiv = document.createElement('div'); | |
| barDiv.classList.add('bar'); | |
| barDiv.id = `bar-${key}`; | |
| barDiv.style.height = '0px'; | |
| const labelSpan = document.createElement('span'); | |
| labelSpan.classList.add('label'); | |
| labelSpan.textContent = key; | |
| columnDiv.appendChild(countSpan); | |
| columnDiv.appendChild(barDiv); | |
| columnDiv.appendChild(labelSpan); | |
| graphContainer.appendChild(columnDiv); | |
| } | |
| } | |
| // 2. Digital Root Calculation | |
| function getDigitalRoot(num) { | |
| if (num === 0) return 9; // As per problem, map 0 to column 9 (1-9 range) | |
| let n = num; | |
| while (n >= 10) { | |
| n = String(n) | |
| .split('') | |
| .reduce((sum, digit) => sum + parseInt(digit), 0); | |
| } | |
| return n; // Will be 1-9 (for n > 0 original input) | |
| } | |
| // 4. Discarding Elements | |
| function discardOldest() { | |
| for (let i = 1; i <= 9; i++) { | |
| const key = i.toString(); | |
| if (dataStore[key] && dataStore[key].length > 0) { | |
| dataStore[key].shift(); // Remove the first element | |
| } | |
| } | |
| } | |
| // 5. Graph Update Function | |
| function updateGraph() { | |
| let overallMaxCount = 0; // Find max count across all arrays for scaling | |
| for (let i = 1; i <= 9; i++) { | |
| const count = dataStore[i.toString()]?.length || 0; | |
| if (count > overallMaxCount) { | |
| overallMaxCount = count; | |
| } | |
| } | |
| // To prevent division by zero if all counts are 0, and ensure bars are 0 height then | |
| const scalingFactor = overallMaxCount > 0 ? MAX_BAR_HEIGHT_PX / overallMaxCount : 0; | |
| for (let i = 1; i <= 9; i++) { | |
| const key = i.toString(); | |
| const count = dataStore[key]?.length || 0; | |
| const barElement = document.getElementById(`bar-${key}`); | |
| const countElement = document.getElementById(`count-${key}`); | |
| if (barElement && countElement) { | |
| const barHeight = Math.min(MAX_BAR_HEIGHT_PX, count * scalingFactor); | |
| barElement.style.height = `${barHeight}px`; | |
| countElement.textContent = count; | |
| } | |
| } | |
| } | |
| // 6. Control Functions | |
| function startSimulation() { | |
| if (generatorIntervalId !== null) return; // Already running | |
| console.log('Starting simulation...'); | |
| initialize(); // Reset graph and data | |
| updateGraph(); // Initial render of empty graph | |
| generatorIntervalId = setInterval(generateAndAppend, GENERATION_INTERVAL_MS); | |
| discarderIntervalId = setInterval(discardOldest, DISCARD_INTERVAL_MS); | |
| graphUpdateIntervalId = setInterval(updateGraph, GRAPH_UPDATE_INTERVAL_MS); | |
| document.getElementById('startButton').disabled = true; | |
| document.getElementById('stopButton').disabled = false; | |
| } | |
| function stopSimulation() { | |
| if (generatorIntervalId === null) return; // Already stopped | |
| console.log('Stopping simulation...'); | |
| clearInterval(generatorIntervalId); | |
| clearInterval(discarderIntervalId); | |
| clearInterval(graphUpdateIntervalId); | |
| generatorIntervalId = null; | |
| discarderIntervalId = null; | |
| graphUpdateIntervalId = null; | |
| document.getElementById('startButton').disabled = false; | |
| document.getElementById('stopButton').disabled = true; | |
| } | |
| // --- Event Listeners --- | |
| document.getElementById('startButton').addEventListener('click', startSimulation); | |
| document.getElementById('stopButton').addEventListener('click', stopSimulation); | |
| // --- Initial Setup --- | |
| document.addEventListener('DOMContentLoaded', () => { | |
| initialize(); // Set up the graph structure | |
| updateGraph(); // Show initial empty state | |
| document.getElementById('stopButton').disabled = true; // Stop is initially disabled | |
| }); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment