Make odds labels readable
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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.`,
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
26
apps/web/src/format.ts
Normal 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`;
|
||||
}
|
||||
@@ -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`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user