···11+<script lang="ts">
22+ import { coinFlip } from "../scripts/chance";
33+ import {
44+ prefix as getPrefix,
55+ name as createName,
66+ suffix as getSuffix,
77+ } from "../scripts/nicknames";
88+99+ /**
1010+ * Creates a randomized name getter that has a chance to return null.
1111+ * @param maker The callable that gets a randomized name.
1212+ * @param nullChance If set, this is the chance that the result will be null. Defaults to a 50/50 chance.
1313+ */
1414+ const createOptionalNameMaker =
1515+ (maker: () => string, nullChance?: number) => () =>
1616+ coinFlip(nullChance) ? null : maker();
1717+ const createPrefix = createOptionalNameMaker(getPrefix, 0.25);
1818+ const createSuffix = createOptionalNameMaker(getSuffix);
1919+2020+ let prefix = $state(createPrefix());
2121+ let name = $state(createName());
2222+ let suffix = $state(createSuffix());
2323+2424+ const onSubmit = (e: SubmitEvent) => {
2525+ e.preventDefault();
2626+ prefix = createPrefix();
2727+ name = createName();
2828+ suffix = createSuffix();
2929+ };
3030+</script>
3131+3232+<form id="nickname-form" onsubmit={onSubmit}>
3333+ <button type="submit" id="nickname-generate" class="primary"
3434+ >(Re)generate</button
3535+ >
3636+</form>
3737+3838+<output for="nickname-generate">
3939+ <p>
4040+ <strong id="nickname"
4141+ >{#if prefix != null}<u>{prefix}</u>{" "}{/if}<u>{name}</u
4242+ >{#if suffix != null}{" "}<u>{suffix}</u>{/if}</strong
4343+ >
4444+ </p>
4545+</output>
+15
src/pages/wm-nickname.astro
···11+---
22+import BaseLayout from "../layouts/BaseLayout.astro";
33+import WmNickname from "../components/WmNickname.svelte";
44+const title = "Water Margin nickname generator";
55+---
66+77+<BaseLayout title={title}>
88+ <p>
99+ This is a nickname generator inspired by the nicknames of the 108 heroes of
1010+ my favorite book, Water Margin. If you're not satisfied with your nickname
1111+ (there's a chance you'll get neither a prefix nor a suffix), feel free to
1212+ click the regenerate button.
1313+ </p>
1414+ <WmNickname client:only="svelte" />
1515+</BaseLayout>
+23
src/scripts/chance.ts
···11+/**
22+ * @module chance Helpers for randomness.
33+ */
44+55+/**
66+ * Roughly a 50/50 chance of true or false. `chance` is optional, but should be a number between 0 and 1 if set.
77+ */
88+export const coinFlip = (chance?: number): boolean =>
99+ Math.random() <= (chance ?? 0.5);
1010+1111+/**
1212+ * Picks a random item from an array.
1313+ */
1414+export const pick = <T>(array: T[]): T =>
1515+ array[Math.floor(Math.random() * array.length)];
1616+1717+/**
1818+ * Creates function that randomly picks from the same array each time.
1919+ */
2020+export const createPick =
2121+ <T>(array: T[]): (() => T) =>
2222+ () =>
2323+ pick(array);