aad-assignment-1/index.html

259 lines
9.1 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<title>DETI Coin Miner - WebAssembly</title>
<style>
body {
font-family: monospace;
padding: 20px;
max-width: 1200px;
margin: 0 auto;
}
button {
padding: 10px 20px;
margin: 5px;
font-size: 16px;
}
#stats {
margin-top: 20px;
padding: 10px;
background: #f0f0f0;
border-radius: 5px;
box-sizing: border-box;
}
#coins-container {
margin-top: 20px;
}
#coins {
width: 100%;
height: 300px;
padding: 10px;
background: #f9f9f9;
border: 2px solid #ccc;
border-radius: 5px;
font-family: monospace;
font-size: 12px;
overflow-y: auto;
white-space: pre-wrap;
word-wrap: break-word;
box-sizing: border-box;
}
.control-group {
margin: 10px 0;
}
label {
display: inline-block;
width: 200px;
}
h2 {
margin-top: 20px;
margin-bottom: 10px;
}
.coin-entry {
color: #006400;
font-weight: bold;
}
.coin-data {
color: #000080;
}
</style>
</head>
<body>
<h1>DETI Coin Miner (WebAssembly)</h1>
<div class="control-group">
<label>Iterations per batch:</label>
<input type="number" id="batchSize" value="1000000" step="100000">
</div>
<div class="control-group">
<label>Update interval (ms):</label>
<input type="number" id="updateInterval" value="100" step="50">
</div>
<button id="start">Start Mining</button>
<button id="stop">Stop Mining</button>
<button id="reset">Reset</button>
<button id="clearCoins">Clear Coins Display</button>
<div id="stats">
Waiting to start...
</div>
<div id="coins-container">
<h2>Found Coins (<span id="coin-count">0</span>)</h2>
<div id="coins"></div>
</div>
<script src="coin_miner_wasm.js"></script>
<script>
let mining = false;
let Module;
let miningInterval;
let updateInterval;
let lastDisplayedCoinCount = 0;
let pausedStats = false;
CoinMinerModule().then(mod => {
Module = mod;
console.log('WebAssembly module loaded');
document.getElementById('start').onclick = () => {
if (!mining) {
mining = true;
Module._resume_mining();
pausedStats = false;
console.log('Starting mining...');
startMining();
}
};
document.getElementById('stop').onclick = () => {
mining = false;
Module._stop_mining();
clearInterval(miningInterval);
clearInterval(updateInterval);
updateStats();
let currentHTML = document.getElementById('stats').innerHTML;
document.getElementById('stats').innerHTML = currentHTML.replace('Mining Statistics:', 'Mining Statistics (PAUSED):');
pausedStats = true;
console.log('Mining stopped');
};
document.getElementById('reset').onclick = () => {
Module._reset_mining();
mining = false;
pausedStats = false;
clearInterval(miningInterval);
clearInterval(updateInterval);
lastDisplayedCoinCount = 0;
document.getElementById('stats').innerHTML = 'Reset complete. Click Start to begin.';
document.getElementById('coins').innerHTML = '';
document.getElementById('coin-count').textContent = '0';
console.log('Mining reset');
};
document.getElementById('clearCoins').onclick = () => {
document.getElementById('coins').innerHTML = '';
console.log('Coins display cleared');
};
function coinToString(coinPtr) {
let coinStr = '';
for (let i = 0; i < 55; i++) {
const byteIdx = i ^ 3;
const wordIdx = Math.floor(byteIdx / 4);
const byteInWord = byteIdx % 4;
const word = Module.getValue(coinPtr + wordIdx * 4, 'i32');
const byte = (word >> (byteInWord * 8)) & 0xFF;
if (byte >= 32 && byte <= 126) {
coinStr += String.fromCharCode(byte);
} else if (byte === 10) {
coinStr += '\\n';
} else if (byte === 0x80) {
coinStr += '[0x80]';
} else {
coinStr += `[0x${byte.toString(16).padStart(2, '0')}]`;
}
}
return coinStr;
}
function displayNewCoins() {
const totalCoins = Module._get_found_coins_count();
if (totalCoins > lastDisplayedCoinCount) {
const coinsDiv = document.getElementById('coins');
for (let i = lastDisplayedCoinCount; i < totalCoins; i++) {
const coinPtr = Module._get_found_coin(i);
if (coinPtr !== 0) {
const coinStr = coinToString(coinPtr);
const timestamp = new Date().toLocaleTimeString();
const entry = document.createElement('div');
entry.innerHTML = `<span class="coin-entry">[${timestamp}] Coin #${i + 1}:</span> <span class="coin-data">${coinStr}</span>`;
coinsDiv.appendChild(entry);
coinsDiv.scrollTop = coinsDiv.scrollHeight;
}
}
lastDisplayedCoinCount = totalCoins;
document.getElementById('coin-count').textContent = totalCoins;
}
}
function updateStats() {
const attemptsPtr = Module._malloc(8);
const coinsPtr = Module._malloc(4);
const hashRatePtr = Module._malloc(8);
const elapsedPtr = Module._malloc(8);
// Zero-initialize the memory before calling
Module.setValue(attemptsPtr, 0, 'i32');
Module.setValue(attemptsPtr + 4, 0, 'i32');
Module._get_statistics(attemptsPtr, coinsPtr, hashRatePtr, elapsedPtr);
// Read 64-bit unsigned value correctly
// On little-endian, low 32 bits come first
const attemptsLowUnsigned = Module.getValue(attemptsPtr, 'i32') >>> 0;
const attemptsHighUnsigned = Module.getValue(attemptsPtr + 4, 'i32') >>> 0;
// Combine - for display purposes, if high part is 0, just show low part
let attempts;
if (attemptsHighUnsigned === 0) {
attempts = attemptsLowUnsigned;
} else {
// Use BigInt for values > 32 bits
const low = BigInt(attemptsLowUnsigned);
const high = BigInt(attemptsHighUnsigned);
attempts = (high * BigInt(4294967296)) + low;
}
const coins = Module.getValue(coinsPtr, 'i32');
const hashRate = Module.getValue(hashRatePtr, 'double');
const elapsed = Module.getValue(elapsedPtr, 'double');
Module._free(attemptsPtr);
Module._free(coinsPtr);
Module._free(hashRatePtr);
Module._free(elapsedPtr);
document.getElementById('stats').innerHTML = `
<strong>Mining Statistics:</strong><br>
Attempts: ${attempts.toString()}<br>
Coins Found: ${coins}<br>
Hash Rate: ${(hashRate / 1e6).toFixed(2)} MH/s<br>
Elapsed Time: ${elapsed.toFixed(2)} seconds
`;
displayNewCoins();
}
function startMining() {
const batchSize = parseInt(document.getElementById('batchSize').value);
const updateMs = parseInt(document.getElementById('updateInterval').value);
miningInterval = setInterval(() => {
if (!mining) {
clearInterval(miningInterval);
return;
}
Module._mine_coins_wasm(batchSize);
}, 0);
updateInterval = setInterval(updateStats, updateMs);
}
}).catch(err => {
console.error('Failed to load WebAssembly module:', err);
document.getElementById('stats').innerHTML = 'Error loading module: ' + err.message;
});
</script>
</body>
</html>