Make odds labels readable

This commit is contained in:
Dorian
2026-05-06 19:47:29 +01:00
parent fa707e2464
commit 1f86bf9cff
7 changed files with 43 additions and 12 deletions

View File

@@ -27,7 +27,7 @@ let timer: number | null = null;
onMounted(() => {
timer = window.setInterval(() => {
lineIndex.value = (lineIndex.value + 1) % LOTTERY_LINES.length;
}, 7000);
}, 10_000);
});
onUnmounted(() => {
if (timer) clearInterval(timer);

View File

@@ -3,10 +3,11 @@ import { computed } from "vue";
import type { DatumSnapshot, HistoryPoint, MinerStat } from "../types";
import { useRotatingCopy } from "../composables/useRotatingCopy";
import { RACE_SCALE_NOTES, RACE_TITLES } from "../copy";
import { multiple } from "../format";
const props = defineProps<{ snapshot: DatumSnapshot | null; history: HistoryPoint[] }>();
const raceTitle = useRotatingCopy(RACE_TITLES);
const scaleNote = useRotatingCopy(RACE_SCALE_NOTES, 7600);
const scaleNote = useRotatingCopy(RACE_SCALE_NOTES);
const networkEh = computed(() => props.snapshot?.network.hashrateEh ?? 0);
const totalThs = computed(() => props.snapshot?.pool.combinedHashrateThs ?? 0);
const networkMultiple = computed(() => networkEh.value > 0 && totalThs.value > 0 ? (networkEh.value * 1_000_000) / totalThs.value : 0);
@@ -67,7 +68,7 @@ const cosmicRows = computed(() => {
name: "Total Bitcoin network",
value: `${networkEh.value.toFixed(0)} EH/s`,
width: 100,
joke: `${networkMultiple.value.toExponential(2)}x your shelf. rude but factual.`,
joke: `${multiple(networkMultiple.value)} your shelf. rude but factual.`,
},
];
});

View File

@@ -3,6 +3,7 @@ import { computed } from "vue";
import type { DatumSnapshot } from "../types";
import { useRotatingCopy } from "../composables/useRotatingCopy";
import { BLOCK_LABELS, HASHRATE_LABELS, NETWORK_LABELS } from "../copy";
import { multiple } from "../format";
const props = defineProps<{ snapshot: DatumSnapshot | null }>();
@@ -20,8 +21,8 @@ const sharesAccepted = computed(() => props.snapshot?.pool.sharesAccepted ?? 0);
const sharesRejected = computed(() => props.snapshot?.pool.sharesRejected ?? 0);
const blockHeight = computed(() => props.snapshot?.job.blockHeight ?? 0);
const hashrateLabel = useRotatingCopy(HASHRATE_LABELS);
const blockLabel = useRotatingCopy(BLOCK_LABELS, 7200);
const networkLabel = useRotatingCopy(NETWORK_LABELS, 7800);
const blockLabel = useRotatingCopy(BLOCK_LABELS);
const networkLabel = useRotatingCopy(NETWORK_LABELS);
const ageS = computed(() => {
const t = props.snapshot?.fetchedAt;
if (!t) return null;
@@ -50,7 +51,7 @@ const ageS = computed(() => {
</div>
<div class="cell">
<div class="label">network is bigger by</div>
<div class="value glow-red">{{ networkMultiple > 0 ? `${networkMultiple.toExponential(2)}x` : "—" }}</div>
<div class="value glow-red">{{ networkMultiple > 0 ? multiple(networkMultiple) : "—" }}</div>
</div>
<div class="cell">
<div class="label">subscribed</div>

View File

@@ -1,6 +1,7 @@
<script setup lang="ts">
import { computed } from "vue";
import type { DatumSnapshot, HistoryPoint, MinerStat } from "../types";
import { multiple, tinyPercentAsOdds } from "../format";
const props = defineProps<{ snapshot: DatumSnapshot | null; history: HistoryPoint[] }>();
@@ -170,7 +171,7 @@ function compact(n: number, digits = 2): string {
function pct(n: number, digits = 4): string {
if (!Number.isFinite(n) || n <= 0) return "0%";
if (n < 0.0001) return `${n.toExponential(2)}%`;
if (n < 0.001) return tinyPercentAsOdds(n);
return `${n.toFixed(digits)}%`;
}
</script>
@@ -216,7 +217,7 @@ function pct(n: number, digits = 4): string {
</div>
<div>
<span class="label">network bullying</span>
<strong class="glow-red">{{ networkMultiple > 0 ? `${networkMultiple.toExponential(2)}x` : "collecting" }}</strong>
<strong class="glow-red">{{ networkMultiple > 0 ? multiple(networkMultiple) : "collecting" }}</strong>
<small>{{ networkEh > 0 ? `${compact(networkEh, 0)} EH/s globally. easy version: the planet brought a warehouse.` : "tx1138 mempool warming up" }}</small>
</div>
</div>

View File

@@ -1,6 +1,6 @@
import { computed, onMounted, onUnmounted, ref } from "vue";
export function useRotatingCopy(lines: readonly string[], intervalMs = 6500) {
export function useRotatingCopy(lines: readonly string[], intervalMs = 10_000) {
const index = ref(0);
let timer: number | null = null;

26
apps/web/src/format.ts Normal file
View File

@@ -0,0 +1,26 @@
export function compactNumber(n: number, digits = 1): string {
if (!Number.isFinite(n)) return "-";
if (Math.abs(n) >= 1e12) return `${(n / 1e12).toFixed(digits)}T`;
if (Math.abs(n) >= 1e9) return `${(n / 1e9).toFixed(digits)}B`;
if (Math.abs(n) >= 1e6) return `${(n / 1e6).toFixed(digits)}M`;
if (Math.abs(n) >= 1e3) return `${(n / 1e3).toFixed(digits)}K`;
return n.toFixed(digits);
}
export function oneIn(probability: number): string {
if (!Number.isFinite(probability) || probability <= 0) return "basically never";
return `1 in ${compactNumber(1 / probability, 1)}`;
}
export function tinyPercentAsOdds(percent: number): string {
if (!Number.isFinite(percent) || percent <= 0) return "0%";
const probability = percent / 100;
if (percent < 0.001) return oneIn(probability);
if (percent < 1) return `${percent.toFixed(4)}%`;
return `${percent.toFixed(2)}%`;
}
export function multiple(n: number): string {
if (!Number.isFinite(n) || n <= 0) return "-";
return `${compactNumber(n, 1)}x`;
}

View File

@@ -1,3 +1,5 @@
import { oneIn } from "./format";
// All user-facing copy. Lean into the futility — these are 4 hobby boards
// trying to hit a 1-in-quadrillions lottery. Take the piss with affection.
@@ -153,7 +155,7 @@ export const STALE_LINES = [
function formatPct(p: number): string {
if (!isFinite(p) || p <= 0) return "ε";
if (p >= 0.01) return `${(p * 100).toFixed(4)}%`;
return `${(p * 100).toExponential(2)}%`;
return oneIn(p);
}
function humanYears(years: number): string {
@@ -168,8 +170,8 @@ function humanYears(years: number): string {
function ratioVsLightning(oddsPerDay: number): string {
const lightning = 0.00000028;
const r = oddsPerDay / lightning;
if (r < 0.001) return r.toExponential(1);
if (r < 0.001) return `about ${oneIn(1 / r)} as likely`;
if (r < 1) return r.toFixed(3);
if (r < 1000) return r.toFixed(1);
return r.toExponential(1);
return `${(r / 1000).toFixed(1)}K`;
}