mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-10 20:27:02 +01:00
Split the kcd2_dice_solver data tables under the cap
dice.ts 415 -> 93 and badges.ts 369 -> 111, with the literal tables in six new modules. Every previously exported name is still exported from the same path, so no importer or test changed. Two import cycles on the way, both of which tsc and eslint called clean and which only showed up as a runtime TypeError when 11 test files failed with "Cannot read properties of undefined (reading tin)". HEADSTART_POINTS now has its own module so both halves of the badge table can use it at init time. 288 tests passing before and after, coverage still 100% on all four metrics with the new modules measured. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011w2Pj4Ek3ZNMQVYcFHgXDY
This commit is contained in:
parent
7a74804786
commit
c04001329e
18
docs/superpowers/contracts/kcd2-data-split-2026-08-16.json
Normal file
18
docs/superpowers/contracts/kcd2-data-split-2026-08-16.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"title": "Split the kcd2_dice_solver data tables under the 250-line cap",
|
||||
"objective": "Bring src/data/dice.ts (415) and src/data/badges.ts (369) under the cap by extracting their large literal tables, without changing any exported value.",
|
||||
"acceptance_criteria": [
|
||||
"Every file under src/data/ is at most 250 lines.",
|
||||
"The suite still reports exactly 288 passing tests in 15 files -- the same as before the split.",
|
||||
"Coverage stays at 100% for statements, branches, functions and lines. The new modules are inside the coverage include glob, so they are measured, not skipped.",
|
||||
"npm run lint (tsc --noEmit && eslint src) passes with no new suppression and no eslint-disable.",
|
||||
"No import cycle: a module that supplies a value used during another module's initialisation must not import back from it. tsc does NOT catch this - it only appears as a runtime TypeError, so the test suite is the check.",
|
||||
"Every name previously exported from dice.ts and badges.ts is still exported from the same path, so no importer or test needs editing."
|
||||
],
|
||||
"out_of_scope": [
|
||||
"Changing any die weight, badge effect or headstart value.",
|
||||
"The kcd2 core/ and component files, which are separate commits.",
|
||||
"Editing any test."
|
||||
],
|
||||
"verifier": "npm test -- --run (expects exactly 288 passed in 15 files), npm run coverage (expects 100% on all four metrics), npm run lint, and check_file_length.sh over src/data/."
|
||||
}
|
||||
43
docs/superpowers/evidence/kcd2-data-split-2026-08-16.json
Normal file
43
docs/superpowers/evidence/kcd2-data-split-2026-08-16.json
Normal file
@ -0,0 +1,43 @@
|
||||
{
|
||||
"intent": "Bring the kcd2_dice_solver data tables under the 250-line cap without changing any exported value.",
|
||||
"scope": [
|
||||
"kcd2_dice_solver/src/data/dice.ts (415 -> 93) + rawDice.ts, rawDiceAM.ts, rawDiceNZ.ts",
|
||||
"kcd2_dice_solver/src/data/badges.ts (369 -> 111) + badgeTable.ts, badgeTableGold.ts, headstart.ts",
|
||||
"Non-goal: no test edited, no exported value changed"
|
||||
],
|
||||
"changes": [
|
||||
"dice.ts: the 317-line RAW_DICE literal and its RawDie interface moved to rawDice.ts, which was itself still 332 lines, so the 43 entries split in half by id into rawDiceAM.ts and rawDiceNZ.ts. rawDice.ts now just concatenates them. dice.ts keeps normalise() and the derived DICE / DICE_BY_ID exports.",
|
||||
"badges.ts: the 253-line BADGES literal moved to badgeTable.ts, then the gold tier moved on to badgeTableGold.ts. badges.ts keeps the scoring rules, the Badge types and the derived lookups, and re-exports BADGES and HEADSTART_POINTS so data.test.ts imports still resolve.",
|
||||
"HEADSTART_POINTS ended up in its own module, headstart.ts, because both halves of the badge table need it at initialisation time."
|
||||
],
|
||||
"verification": [
|
||||
{
|
||||
"command": "npm test -- --run",
|
||||
"result": "fail twice, then pass",
|
||||
"evidence": "288 passed in 15 files, matching the baseline. Two intermediate failures, both import cycles that tsc reported as clean: first badgeTable importing HEADSTART_POINTS from badges (11 test files failed with 'Cannot read properties of undefined (reading tin)'), then badgeTableGold importing it from badgeTable. Resolved by giving the constant its own module."
|
||||
},
|
||||
{
|
||||
"command": "npm run coverage",
|
||||
"result": "pass",
|
||||
"evidence": "100% statements (863/863), branches (375/375), functions (181/181) and lines (810/810). The new modules fall inside the coverage include glob, so they are measured rather than silently skipped - the mirror of the poker-stakes trap where new files landed in an excluded path."
|
||||
},
|
||||
{
|
||||
"command": "npm run lint",
|
||||
"result": "pass",
|
||||
"evidence": "tsc --noEmit and eslint both clean, after removing one BadgeTier import the split left unused."
|
||||
},
|
||||
{
|
||||
"command": "bash ~/utils/scripts/check_file_length.sh --all | grep kcd2",
|
||||
"result": "pass",
|
||||
"evidence": "No src/data/ file remains. The largest is badgeTable.ts at 175."
|
||||
}
|
||||
],
|
||||
"risks": [
|
||||
"An import cycle in TypeScript type-checks perfectly and fails only at module-init time. It happened twice here and neither tsc nor eslint saw it; only running the suite did. Any further split of these tables must be validated by running the tests, not by a clean lint.",
|
||||
"badges.ts re-exports BADGES and HEADSTART_POINTS purely so existing importers keep working. That indirection is invisible at the call site and easy to delete by accident."
|
||||
],
|
||||
"rollback": [
|
||||
"git revert the commit; dice.ts and badges.ts return to 415 and 369 lines and the six new modules disappear.",
|
||||
"After rollback, re-run npm test and expect 288 passed."
|
||||
]
|
||||
}
|
||||
165
kcd2_dice_solver/src/data/badgeTable.ts
Normal file
165
kcd2_dice_solver/src/data/badgeTable.ts
Normal file
@ -0,0 +1,165 @@
|
||||
/**
|
||||
* The badge table: every badge's id, tier, description and effect.
|
||||
*
|
||||
* Split out of data/badges.ts to keep it under the 250-line cap. Pure data;
|
||||
* badges.ts still owns the scoring rules and the derived lookups.
|
||||
*/
|
||||
|
||||
import type { Badge } from "./badges.ts";
|
||||
import { HEADSTART_POINTS } from "./headstart.ts";
|
||||
import { GOLD_BADGES } from "./badgeTableGold.ts";
|
||||
|
||||
export const BADGES: readonly Badge[] = [
|
||||
// ---- Tin ---------------------------------------------------------------
|
||||
{
|
||||
id: "carpenters_advantage",
|
||||
name: "Carpenter's Advantage badge",
|
||||
description: "You gain a new dice formation called the Cut, consisting of 3+5.",
|
||||
tier: "tin",
|
||||
effect: { kind: "scoring", rules: { cut: true } },
|
||||
},
|
||||
{
|
||||
id: "tin_defence",
|
||||
name: "Tin Defence badge",
|
||||
description: "Use to cancel the effect of your opponent's tin badge.",
|
||||
tier: "tin",
|
||||
effect: { kind: "defence" },
|
||||
},
|
||||
{
|
||||
id: "tin_doppelganger",
|
||||
name: "Tin Doppelganger badge",
|
||||
description: "Using it will double the score of your last roll. Can be used once per game.",
|
||||
tier: "tin",
|
||||
effect: { kind: "doubleThrow", uses: 1 },
|
||||
},
|
||||
{
|
||||
id: "tin_fortune",
|
||||
name: "Tin Fortune badge",
|
||||
description:
|
||||
"After your throw, you can reroll a die of your choosing. Can be used once per game.",
|
||||
tier: "tin",
|
||||
effect: { kind: "reroll", dice: 1, uses: 1 },
|
||||
},
|
||||
{
|
||||
id: "tin_headstart",
|
||||
name: "Tin Headstart badge",
|
||||
description: "You gain a small point lead at the start of the game.",
|
||||
tier: "tin",
|
||||
effect: { kind: "headstart", points: HEADSTART_POINTS.tin },
|
||||
},
|
||||
{
|
||||
id: "tin_might",
|
||||
name: "Tin Might badge",
|
||||
description: "Use it to add one extra die to your throw. Can be used once per game.",
|
||||
tier: "tin",
|
||||
effect: { kind: "extraDice", dice: 1, uses: 1 },
|
||||
},
|
||||
{
|
||||
id: "tin_resurrection",
|
||||
name: "Tin Resurrection badge",
|
||||
description: "After an unlucky throw, use this badge to throw again. Can be used once per game.",
|
||||
tier: "tin",
|
||||
effect: { kind: "antibust", uses: 1 },
|
||||
},
|
||||
{
|
||||
id: "tin_transmutation",
|
||||
name: "Tin Transmutation badge",
|
||||
description:
|
||||
"After your throw, you can change a die of your choosing to a 3. Can be used once per game.",
|
||||
tier: "tin",
|
||||
effect: { kind: "setDie", value: 3, uses: 1 },
|
||||
},
|
||||
{
|
||||
id: "tin_warlord",
|
||||
name: "Tin Warlord badge",
|
||||
description: "Used to gain a quarter more points from your turn. Can be used once per game.",
|
||||
tier: "tin",
|
||||
effect: { kind: "multiplier", factor: 1.25, uses: 1 },
|
||||
},
|
||||
|
||||
// ---- Silver ------------------------------------------------------------
|
||||
{
|
||||
id: "bird_kings",
|
||||
name: "Bird king's badge",
|
||||
description:
|
||||
"The badge of the rightful king of the birds will allow you to roll an additional die. Can be used twice per game.",
|
||||
tier: "silver",
|
||||
effect: { kind: "extraDice", dice: 1, uses: 2 },
|
||||
},
|
||||
{
|
||||
id: "executioners_advantage",
|
||||
name: "Executioner's Advantage badge",
|
||||
description:
|
||||
"You gain a new dice combination called The Gallows, which consists of 4, 5 and 6.",
|
||||
tier: "silver",
|
||||
effect: { kind: "scoring", rules: { gallows: true } },
|
||||
},
|
||||
{
|
||||
id: "silver_defence",
|
||||
name: "Silver Defence badge",
|
||||
description: "Use to cancel the effect of your opponent's Silver badge in the game.",
|
||||
tier: "silver",
|
||||
effect: { kind: "defence" },
|
||||
},
|
||||
{
|
||||
id: "silver_doppelganger",
|
||||
name: "Silver Doppelganger badge",
|
||||
description: "You double the score of your last throw. Can be used twice per game.",
|
||||
tier: "silver",
|
||||
effect: { kind: "doubleThrow", uses: 2 },
|
||||
},
|
||||
{
|
||||
id: "silver_fortune",
|
||||
name: "Silver Fortune badge",
|
||||
description: "After your throw, you can reroll up to 2 dice. Can be used once per game.",
|
||||
tier: "silver",
|
||||
effect: { kind: "reroll", dice: 2, uses: 1 },
|
||||
},
|
||||
{
|
||||
id: "silver_headstart",
|
||||
name: "Silver Headstart badge",
|
||||
description: "Use it to get a moderate point lead at the start of the game.",
|
||||
tier: "silver",
|
||||
effect: { kind: "headstart", points: HEADSTART_POINTS.silver },
|
||||
},
|
||||
{
|
||||
id: "silver_might",
|
||||
name: "Silver Might badge",
|
||||
description: "Using it will allow you to roll one extra die. Can be used twice per game.",
|
||||
tier: "silver",
|
||||
effect: { kind: "extraDice", dice: 1, uses: 2 },
|
||||
},
|
||||
{
|
||||
id: "silver_resurrection",
|
||||
name: "Silver Resurrection badge",
|
||||
description:
|
||||
"When a throw doesn't go your way, use this badge to throw again. Can be used twice per game.",
|
||||
tier: "silver",
|
||||
effect: { kind: "antibust", uses: 2 },
|
||||
},
|
||||
{
|
||||
id: "silver_swap_out",
|
||||
name: "Silver Swap-out badge",
|
||||
description:
|
||||
"After your throw, you can reroll a die of your choosing. Can be used once per game.",
|
||||
tier: "silver",
|
||||
effect: { kind: "reroll", dice: 1, uses: 1 },
|
||||
},
|
||||
{
|
||||
id: "silver_transmutation",
|
||||
name: "Silver Transmutation badge",
|
||||
description:
|
||||
"After your throw, you can change a die of your choosing to a five. Can be used once per game.",
|
||||
tier: "silver",
|
||||
effect: { kind: "setDie", value: 5, uses: 1 },
|
||||
},
|
||||
{
|
||||
id: "silver_warlord",
|
||||
name: "Silver Warlord badge",
|
||||
description:
|
||||
"Using this badge will grant you 50% more points this round. Can be used once per game.",
|
||||
tier: "silver",
|
||||
effect: { kind: "multiplier", factor: 1.5, uses: 1 },
|
||||
},
|
||||
...GOLD_BADGES,
|
||||
];
|
||||
108
kcd2_dice_solver/src/data/badgeTableGold.ts
Normal file
108
kcd2_dice_solver/src/data/badgeTableGold.ts
Normal file
@ -0,0 +1,108 @@
|
||||
/**
|
||||
* The gold-tier badges.
|
||||
*
|
||||
* Split out of data/badgeTable.ts to keep it under the 250-line cap.
|
||||
*/
|
||||
|
||||
import type { Badge } from "./badges.ts";
|
||||
import { HEADSTART_POINTS } from "./headstart.ts";
|
||||
|
||||
export const GOLD_BADGES: readonly Badge[] = [
|
||||
// ---- Gold --------------------------------------------------------------
|
||||
{
|
||||
id: "gold_defence",
|
||||
name: "Gold Defence badge",
|
||||
description: "Use to cancel the effect of your opponent's gold badge.",
|
||||
tier: "gold",
|
||||
effect: { kind: "defence" },
|
||||
},
|
||||
{
|
||||
id: "gold_doppelganger",
|
||||
name: "Gold Doppelganger badge",
|
||||
description:
|
||||
"Using this badge will double the point value of your last throw. Can be used three times per game.",
|
||||
tier: "gold",
|
||||
effect: { kind: "doubleThrow", uses: 3 },
|
||||
},
|
||||
{
|
||||
id: "gold_emperors",
|
||||
name: "Gold Emperor's badge",
|
||||
description:
|
||||
"Using this badge, you will gain triple points for every 1+1+1 dice combination. Emperors don't lose.",
|
||||
tier: "gold",
|
||||
effect: { kind: "scoring", rules: { emperorTriple: true } },
|
||||
},
|
||||
{
|
||||
id: "gold_fortune",
|
||||
name: "Gold Fortune badge",
|
||||
description: "After your throw, you can reroll up to three dice. Can be used once per game.",
|
||||
tier: "gold",
|
||||
effect: { kind: "reroll", dice: 3, uses: 1 },
|
||||
},
|
||||
{
|
||||
id: "gold_headstart",
|
||||
name: "Gold Headstart badge",
|
||||
description: "Using this badge will give you a large point lead at the start of the game.",
|
||||
tier: "gold",
|
||||
effect: { kind: "headstart", points: HEADSTART_POINTS.gold },
|
||||
},
|
||||
{
|
||||
id: "gold_might",
|
||||
name: "Gold Might badge",
|
||||
description: "Using it will allow you to roll one extra die. Can be used three times per game.",
|
||||
tier: "gold",
|
||||
effect: { kind: "extraDice", dice: 1, uses: 3 },
|
||||
},
|
||||
{
|
||||
id: "gold_resurrection",
|
||||
name: "Gold Resurrection badge",
|
||||
description: "After an unlucky throw, you can throw again. Can be used three times per game.",
|
||||
tier: "gold",
|
||||
effect: { kind: "antibust", uses: 3 },
|
||||
},
|
||||
{
|
||||
id: "gold_swap_out",
|
||||
name: "Gold Swap-out badge",
|
||||
description:
|
||||
"After your throw, you can reroll two dice with the same value. Can be used once per game.",
|
||||
tier: "gold",
|
||||
effect: { kind: "reroll", dice: 2, uses: 1 },
|
||||
},
|
||||
{
|
||||
id: "gold_transmutation",
|
||||
name: "Gold Transmutation badge",
|
||||
description: "After your throw, change a die of your choosing to a 1. Can be used once per game.",
|
||||
tier: "gold",
|
||||
effect: { kind: "setDie", value: 1, uses: 1 },
|
||||
},
|
||||
{
|
||||
id: "gold_tyche",
|
||||
name: "Gold Tyche badge",
|
||||
description: "If you roll three sixes in a game, you'll always earn double points for them.",
|
||||
tier: "gold",
|
||||
effect: { kind: "scoring", rules: { tycheDouble: true } },
|
||||
},
|
||||
{
|
||||
id: "gold_warlord",
|
||||
name: "Gold Warlord badge",
|
||||
description: "You double the score of your turn. Can be used once per game.",
|
||||
tier: "gold",
|
||||
effect: { kind: "multiplier", factor: 2, uses: 1 },
|
||||
},
|
||||
{
|
||||
id: "gold_wedding",
|
||||
name: "Gold Wedding badge",
|
||||
description:
|
||||
"A memento of Agnes and Olda's big day. Using it allows you to reroll up to three dice. Can be used once per game.",
|
||||
tier: "gold",
|
||||
effect: { kind: "reroll", dice: 3, uses: 1 },
|
||||
},
|
||||
{
|
||||
id: "priests_advantage",
|
||||
name: "Priest's Advantage badge",
|
||||
description:
|
||||
"You gain a new dice formation called The Eye, consisting of the values 1, 3 and 5.",
|
||||
tier: "gold",
|
||||
effect: { kind: "scoring", rules: { eye: true } },
|
||||
},
|
||||
];
|
||||
@ -68,17 +68,6 @@ export const DEFAULT_FORMATION_VALUES: FormationValues = {
|
||||
eye: 300,
|
||||
};
|
||||
|
||||
/**
|
||||
* Point leads granted by the Headstart badges.
|
||||
*
|
||||
* UNVERIFIED. The wiki only says "small" / "moderate" / "large".
|
||||
*/
|
||||
export const HEADSTART_POINTS: Readonly<Record<BadgeTier, number>> = {
|
||||
tin: 250,
|
||||
silver: 500,
|
||||
gold: 1000,
|
||||
};
|
||||
|
||||
/** What a badge actually does, as a discriminated union. */
|
||||
export type BadgeEffect =
|
||||
/** Changes the scoring table. Evaluated jointly with the dice set. */
|
||||
@ -108,257 +97,11 @@ export interface Badge {
|
||||
readonly effect: BadgeEffect;
|
||||
}
|
||||
|
||||
export const BADGES: readonly Badge[] = [
|
||||
// ---- Tin ---------------------------------------------------------------
|
||||
{
|
||||
id: "carpenters_advantage",
|
||||
name: "Carpenter's Advantage badge",
|
||||
description: "You gain a new dice formation called the Cut, consisting of 3+5.",
|
||||
tier: "tin",
|
||||
effect: { kind: "scoring", rules: { cut: true } },
|
||||
},
|
||||
{
|
||||
id: "tin_defence",
|
||||
name: "Tin Defence badge",
|
||||
description: "Use to cancel the effect of your opponent's tin badge.",
|
||||
tier: "tin",
|
||||
effect: { kind: "defence" },
|
||||
},
|
||||
{
|
||||
id: "tin_doppelganger",
|
||||
name: "Tin Doppelganger badge",
|
||||
description: "Using it will double the score of your last roll. Can be used once per game.",
|
||||
tier: "tin",
|
||||
effect: { kind: "doubleThrow", uses: 1 },
|
||||
},
|
||||
{
|
||||
id: "tin_fortune",
|
||||
name: "Tin Fortune badge",
|
||||
description:
|
||||
"After your throw, you can reroll a die of your choosing. Can be used once per game.",
|
||||
tier: "tin",
|
||||
effect: { kind: "reroll", dice: 1, uses: 1 },
|
||||
},
|
||||
{
|
||||
id: "tin_headstart",
|
||||
name: "Tin Headstart badge",
|
||||
description: "You gain a small point lead at the start of the game.",
|
||||
tier: "tin",
|
||||
effect: { kind: "headstart", points: HEADSTART_POINTS.tin },
|
||||
},
|
||||
{
|
||||
id: "tin_might",
|
||||
name: "Tin Might badge",
|
||||
description: "Use it to add one extra die to your throw. Can be used once per game.",
|
||||
tier: "tin",
|
||||
effect: { kind: "extraDice", dice: 1, uses: 1 },
|
||||
},
|
||||
{
|
||||
id: "tin_resurrection",
|
||||
name: "Tin Resurrection badge",
|
||||
description: "After an unlucky throw, use this badge to throw again. Can be used once per game.",
|
||||
tier: "tin",
|
||||
effect: { kind: "antibust", uses: 1 },
|
||||
},
|
||||
{
|
||||
id: "tin_transmutation",
|
||||
name: "Tin Transmutation badge",
|
||||
description:
|
||||
"After your throw, you can change a die of your choosing to a 3. Can be used once per game.",
|
||||
tier: "tin",
|
||||
effect: { kind: "setDie", value: 3, uses: 1 },
|
||||
},
|
||||
{
|
||||
id: "tin_warlord",
|
||||
name: "Tin Warlord badge",
|
||||
description: "Used to gain a quarter more points from your turn. Can be used once per game.",
|
||||
tier: "tin",
|
||||
effect: { kind: "multiplier", factor: 1.25, uses: 1 },
|
||||
},
|
||||
import { BADGES } from "./badgeTable.ts";
|
||||
import { HEADSTART_POINTS } from "./headstart.ts";
|
||||
|
||||
// ---- Silver ------------------------------------------------------------
|
||||
{
|
||||
id: "bird_kings",
|
||||
name: "Bird king's badge",
|
||||
description:
|
||||
"The badge of the rightful king of the birds will allow you to roll an additional die. Can be used twice per game.",
|
||||
tier: "silver",
|
||||
effect: { kind: "extraDice", dice: 1, uses: 2 },
|
||||
},
|
||||
{
|
||||
id: "executioners_advantage",
|
||||
name: "Executioner's Advantage badge",
|
||||
description:
|
||||
"You gain a new dice combination called The Gallows, which consists of 4, 5 and 6.",
|
||||
tier: "silver",
|
||||
effect: { kind: "scoring", rules: { gallows: true } },
|
||||
},
|
||||
{
|
||||
id: "silver_defence",
|
||||
name: "Silver Defence badge",
|
||||
description: "Use to cancel the effect of your opponent's Silver badge in the game.",
|
||||
tier: "silver",
|
||||
effect: { kind: "defence" },
|
||||
},
|
||||
{
|
||||
id: "silver_doppelganger",
|
||||
name: "Silver Doppelganger badge",
|
||||
description: "You double the score of your last throw. Can be used twice per game.",
|
||||
tier: "silver",
|
||||
effect: { kind: "doubleThrow", uses: 2 },
|
||||
},
|
||||
{
|
||||
id: "silver_fortune",
|
||||
name: "Silver Fortune badge",
|
||||
description: "After your throw, you can reroll up to 2 dice. Can be used once per game.",
|
||||
tier: "silver",
|
||||
effect: { kind: "reroll", dice: 2, uses: 1 },
|
||||
},
|
||||
{
|
||||
id: "silver_headstart",
|
||||
name: "Silver Headstart badge",
|
||||
description: "Use it to get a moderate point lead at the start of the game.",
|
||||
tier: "silver",
|
||||
effect: { kind: "headstart", points: HEADSTART_POINTS.silver },
|
||||
},
|
||||
{
|
||||
id: "silver_might",
|
||||
name: "Silver Might badge",
|
||||
description: "Using it will allow you to roll one extra die. Can be used twice per game.",
|
||||
tier: "silver",
|
||||
effect: { kind: "extraDice", dice: 1, uses: 2 },
|
||||
},
|
||||
{
|
||||
id: "silver_resurrection",
|
||||
name: "Silver Resurrection badge",
|
||||
description:
|
||||
"When a throw doesn't go your way, use this badge to throw again. Can be used twice per game.",
|
||||
tier: "silver",
|
||||
effect: { kind: "antibust", uses: 2 },
|
||||
},
|
||||
{
|
||||
id: "silver_swap_out",
|
||||
name: "Silver Swap-out badge",
|
||||
description:
|
||||
"After your throw, you can reroll a die of your choosing. Can be used once per game.",
|
||||
tier: "silver",
|
||||
effect: { kind: "reroll", dice: 1, uses: 1 },
|
||||
},
|
||||
{
|
||||
id: "silver_transmutation",
|
||||
name: "Silver Transmutation badge",
|
||||
description:
|
||||
"After your throw, you can change a die of your choosing to a five. Can be used once per game.",
|
||||
tier: "silver",
|
||||
effect: { kind: "setDie", value: 5, uses: 1 },
|
||||
},
|
||||
{
|
||||
id: "silver_warlord",
|
||||
name: "Silver Warlord badge",
|
||||
description:
|
||||
"Using this badge will grant you 50% more points this round. Can be used once per game.",
|
||||
tier: "silver",
|
||||
effect: { kind: "multiplier", factor: 1.5, uses: 1 },
|
||||
},
|
||||
export { BADGES, HEADSTART_POINTS };
|
||||
|
||||
// ---- Gold --------------------------------------------------------------
|
||||
{
|
||||
id: "gold_defence",
|
||||
name: "Gold Defence badge",
|
||||
description: "Use to cancel the effect of your opponent's gold badge.",
|
||||
tier: "gold",
|
||||
effect: { kind: "defence" },
|
||||
},
|
||||
{
|
||||
id: "gold_doppelganger",
|
||||
name: "Gold Doppelganger badge",
|
||||
description:
|
||||
"Using this badge will double the point value of your last throw. Can be used three times per game.",
|
||||
tier: "gold",
|
||||
effect: { kind: "doubleThrow", uses: 3 },
|
||||
},
|
||||
{
|
||||
id: "gold_emperors",
|
||||
name: "Gold Emperor's badge",
|
||||
description:
|
||||
"Using this badge, you will gain triple points for every 1+1+1 dice combination. Emperors don't lose.",
|
||||
tier: "gold",
|
||||
effect: { kind: "scoring", rules: { emperorTriple: true } },
|
||||
},
|
||||
{
|
||||
id: "gold_fortune",
|
||||
name: "Gold Fortune badge",
|
||||
description: "After your throw, you can reroll up to three dice. Can be used once per game.",
|
||||
tier: "gold",
|
||||
effect: { kind: "reroll", dice: 3, uses: 1 },
|
||||
},
|
||||
{
|
||||
id: "gold_headstart",
|
||||
name: "Gold Headstart badge",
|
||||
description: "Using this badge will give you a large point lead at the start of the game.",
|
||||
tier: "gold",
|
||||
effect: { kind: "headstart", points: HEADSTART_POINTS.gold },
|
||||
},
|
||||
{
|
||||
id: "gold_might",
|
||||
name: "Gold Might badge",
|
||||
description: "Using it will allow you to roll one extra die. Can be used three times per game.",
|
||||
tier: "gold",
|
||||
effect: { kind: "extraDice", dice: 1, uses: 3 },
|
||||
},
|
||||
{
|
||||
id: "gold_resurrection",
|
||||
name: "Gold Resurrection badge",
|
||||
description: "After an unlucky throw, you can throw again. Can be used three times per game.",
|
||||
tier: "gold",
|
||||
effect: { kind: "antibust", uses: 3 },
|
||||
},
|
||||
{
|
||||
id: "gold_swap_out",
|
||||
name: "Gold Swap-out badge",
|
||||
description:
|
||||
"After your throw, you can reroll two dice with the same value. Can be used once per game.",
|
||||
tier: "gold",
|
||||
effect: { kind: "reroll", dice: 2, uses: 1 },
|
||||
},
|
||||
{
|
||||
id: "gold_transmutation",
|
||||
name: "Gold Transmutation badge",
|
||||
description: "After your throw, change a die of your choosing to a 1. Can be used once per game.",
|
||||
tier: "gold",
|
||||
effect: { kind: "setDie", value: 1, uses: 1 },
|
||||
},
|
||||
{
|
||||
id: "gold_tyche",
|
||||
name: "Gold Tyche badge",
|
||||
description: "If you roll three sixes in a game, you'll always earn double points for them.",
|
||||
tier: "gold",
|
||||
effect: { kind: "scoring", rules: { tycheDouble: true } },
|
||||
},
|
||||
{
|
||||
id: "gold_warlord",
|
||||
name: "Gold Warlord badge",
|
||||
description: "You double the score of your turn. Can be used once per game.",
|
||||
tier: "gold",
|
||||
effect: { kind: "multiplier", factor: 2, uses: 1 },
|
||||
},
|
||||
{
|
||||
id: "gold_wedding",
|
||||
name: "Gold Wedding badge",
|
||||
description:
|
||||
"A memento of Agnes and Olda's big day. Using it allows you to reroll up to three dice. Can be used once per game.",
|
||||
tier: "gold",
|
||||
effect: { kind: "reroll", dice: 3, uses: 1 },
|
||||
},
|
||||
{
|
||||
id: "priests_advantage",
|
||||
name: "Priest's Advantage badge",
|
||||
description:
|
||||
"You gain a new dice formation called The Eye, consisting of the values 1, 3 and 5.",
|
||||
tier: "gold",
|
||||
effect: { kind: "scoring", rules: { eye: true } },
|
||||
},
|
||||
];
|
||||
|
||||
/** Lookup from badge id to badge. */
|
||||
export const BADGES_BY_ID: ReadonlyMap<string, Badge> = new Map(
|
||||
|
||||
@ -65,330 +65,8 @@ export function normalise(raw: Weights): Weights {
|
||||
];
|
||||
}
|
||||
|
||||
interface RawDie {
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly description: string;
|
||||
readonly weights: Weights;
|
||||
readonly wildcardFaces?: readonly Face[];
|
||||
readonly wildScoresAlone?: boolean;
|
||||
}
|
||||
|
||||
const RAW_DICE: readonly RawDie[] = [
|
||||
{
|
||||
id: "aranka",
|
||||
name: "Aranka's die",
|
||||
description:
|
||||
"Aranka gave me this die to make it easier for me to play against her husband.",
|
||||
weights: [28.6, 4.8, 28.6, 4.8, 28.6, 4.8],
|
||||
},
|
||||
{
|
||||
id: "balatro",
|
||||
name: "Balatro's die",
|
||||
description:
|
||||
"A die crafted by the balatro Jimbo, marked with his grinning face. When it lands, you get to choose how it's counted!",
|
||||
// Only ONE face is the joker — Jimbo's grinning face, visible on the item
|
||||
// icon. The wiki's dice-effect table for this die is `{{Dice effect|d|d|d|d|
|
||||
// d|d}}`: six *unfilled placeholders* that render as "d%", not six wildcards.
|
||||
// Reading them as wildcards is what made this die look unbeatable.
|
||||
//
|
||||
// No source publishes its side weights (the wiki cells are the placeholders
|
||||
// above and Inara omits the die entirely), so uniform is an assumption, kept
|
||||
// as [1,1,1,1,1,1] rather than a fake "16.7" so it cannot be mistaken for a
|
||||
// transcribed figure.
|
||||
//
|
||||
// The joker replaces the one — confirmed by the player, who owns the game —
|
||||
// like the Devil's head die it is explicitly compared to. That is also why
|
||||
// holding it alone counts as a 1: the joker sits where the 1 used to be, so
|
||||
// the die has no plain single pip at all.
|
||||
weights: [1, 1, 1, 1, 1, 1],
|
||||
wildcardFaces: [1],
|
||||
// "When it lands, you get to choose how it's counted!" — and, unlike the
|
||||
// Devil's head, "picking it alone will count as if you threw 1".
|
||||
wildScoresAlone: true,
|
||||
},
|
||||
{
|
||||
id: "cautious_cheater",
|
||||
name: "Cautious cheater's die",
|
||||
description:
|
||||
"A die modified by an expert. It is precisely loaded, but also inconspicuous.",
|
||||
weights: [23.8, 14.3, 9.5, 14.3, 23.8, 14.3],
|
||||
},
|
||||
{
|
||||
id: "ci",
|
||||
name: "Ci die",
|
||||
description:
|
||||
"The second in the line of the demonic dice, she likes to get lost, but when she's with her sisters she's very strong.",
|
||||
// Inara: 14.3 / 14.3 / 14.3 / 14.3 / 14.3 / 28.6
|
||||
weights: [13, 13, 13, 13, 13, 34.8],
|
||||
},
|
||||
{
|
||||
id: "devils_head",
|
||||
name: "Devil's head die",
|
||||
description:
|
||||
"A die that feels hot to the touch. In place of a one it has a devil's head, which is not something folk like to gaze upon…",
|
||||
// Face 1 is the devil's head, which the wiki scoring table marks "Subst" —
|
||||
// it substitutes for any value, but per the die's own page it "acts as a
|
||||
// joker, matching any combination but never scoring on its own", so it
|
||||
// cannot be the lone 1 or 5 that lets you hold a die.
|
||||
weights: [16.7, 16.7, 16.7, 16.7, 16.7, 16.7],
|
||||
wildcardFaces: [1],
|
||||
},
|
||||
{
|
||||
id: "misfortune",
|
||||
name: "Die of misfortune",
|
||||
description:
|
||||
"They say that when it rains, it pours. But if you play with this die, the only thing pouring will be your tears.",
|
||||
weights: [4.5, 22.7, 22.7, 22.7, 22.7, 4.5],
|
||||
},
|
||||
{
|
||||
id: "even",
|
||||
name: "Even die",
|
||||
description: "A die loaded in favour of even numbers.",
|
||||
weights: [6.7, 26.7, 6.7, 26.7, 6.7, 26.7],
|
||||
},
|
||||
{
|
||||
id: "favourable",
|
||||
name: "Favourable die",
|
||||
description: "A playing die that brings luck more often than you'd expect.",
|
||||
weights: [33.3, 0, 5.6, 5.6, 33.3, 22.2],
|
||||
},
|
||||
{
|
||||
id: "fer",
|
||||
name: "Fer die",
|
||||
description: "The third and last in the line of demonic dice.",
|
||||
// Inara: 14.3 / 14.3 / 14.3 / 14.3 / 14.3 / 28.6
|
||||
weights: [13, 13, 13, 13, 13, 34.8],
|
||||
},
|
||||
{
|
||||
id: "greasy",
|
||||
name: "Greasy die",
|
||||
description:
|
||||
"A more reliable die than a normal one, but it cannot be relied on for everything.",
|
||||
weights: [17.6, 11.8, 17.6, 11.7, 17.6, 23.5],
|
||||
},
|
||||
{
|
||||
id: "grimy",
|
||||
name: "Grimy die",
|
||||
description:
|
||||
"One could say it will get you out of the frying pan into the fire. And sometimes it will let you stew in your own juice.",
|
||||
weights: [6.2, 31.2, 6.2, 6.2, 43.7, 6.2],
|
||||
},
|
||||
{
|
||||
id: "grozav",
|
||||
name: "Grozav's lucky die",
|
||||
description: "It is actually not all that lucky, but don't tell anyone!",
|
||||
weights: [6.7, 66.7, 6.7, 6.7, 6.7, 6.7],
|
||||
},
|
||||
{
|
||||
id: "heavenly_kingdom",
|
||||
name: "Heavenly Kingdom die",
|
||||
description:
|
||||
"A miraculous playing die, sent from the Heavenly Kingdom to the kingdom of men.",
|
||||
weights: [36.8, 10.5, 10.5, 10.5, 10.5, 21],
|
||||
},
|
||||
{
|
||||
id: "holy_trinity",
|
||||
name: "Holy Trinity die",
|
||||
description:
|
||||
"A consecrated die commemorating the Holy Trinity, especially by rolling threes.",
|
||||
// Inara: 21.1 / 26.3 / 36.8 / 5.3 / 5.3 / 5.3
|
||||
weights: [18.2, 22.7, 45.4, 4.5, 4.5, 4.5],
|
||||
},
|
||||
{
|
||||
id: "hugo",
|
||||
name: "Hugo's die",
|
||||
description: "A die of the most loyal regular at the Hole. It bears his likeness.",
|
||||
weights: [16.7, 16.7, 16.7, 16.7, 16.7, 16.7],
|
||||
},
|
||||
{
|
||||
id: "kings",
|
||||
name: "King's die",
|
||||
description:
|
||||
"An anointed head has no need of a loaded die, since he is always right and can never lose.",
|
||||
weights: [12.5, 18.7, 21.9, 25, 12.5, 9.4],
|
||||
},
|
||||
{
|
||||
id: "lousy_gambler",
|
||||
name: "Lousy gambler's die",
|
||||
description: "A shoddy loaded die. It's quite noticeably unbalanced.",
|
||||
weights: [10, 15, 10, 15, 35, 15],
|
||||
},
|
||||
{
|
||||
id: "lu",
|
||||
name: "Lu die",
|
||||
description: "The first of the line of demonic dice.",
|
||||
// Inara: 14.3 / 14.3 / 14.3 / 14.3 / 14.3 / 28.6
|
||||
weights: [13, 13, 13, 13, 13, 34.8],
|
||||
},
|
||||
{
|
||||
id: "lucky",
|
||||
name: "Lucky die",
|
||||
description:
|
||||
"When fortune smiles on you, smile back. Otherwise you'll look suspicious.",
|
||||
weights: [27.3, 4.5, 9.1, 13.6, 18.2, 27.3],
|
||||
},
|
||||
{
|
||||
id: "mathematician",
|
||||
name: "Mathematician's die",
|
||||
description:
|
||||
"A die loaded based on the work of a forgotten mathematician. It may be better suited to solving equations than playing dice.",
|
||||
weights: [16.7, 20.8, 25, 29.2, 4.2, 4.2],
|
||||
},
|
||||
{
|
||||
id: "molar",
|
||||
name: "Molar die",
|
||||
description:
|
||||
"A die made out of a molar tooth. It's probably better not to know who it came from.",
|
||||
weights: [16.7, 16.7, 16.7, 16.7, 16.7, 16.7],
|
||||
},
|
||||
{
|
||||
id: "monk",
|
||||
name: "Monk's die",
|
||||
description:
|
||||
"No one knows for sure whether it was blessed by a holy man or just spat on by a drunken monk. Either way, the die rolls... sort of.",
|
||||
weights: [40, 40, 5, 5, 5, 5],
|
||||
},
|
||||
{
|
||||
id: "mother_of_pearl",
|
||||
name: "Mother-of-pearl die",
|
||||
description:
|
||||
"A rare pink mother-of-pearl die. Said to bring luck in games, but misfortune in love.",
|
||||
weights: [25, 8.3, 8.3, 8.3, 25, 25],
|
||||
},
|
||||
{
|
||||
id: "odd",
|
||||
name: "Odd die",
|
||||
description: "A die loaded to favour odd numbers.",
|
||||
weights: [26.7, 6.7, 26.7, 6.7, 26.7, 6.7],
|
||||
},
|
||||
{
|
||||
id: "ordinary",
|
||||
name: "Ordinary die",
|
||||
description: "An ordinary playing die.",
|
||||
weights: [16.7, 16.7, 16.7, 16.7, 16.7, 16.7],
|
||||
},
|
||||
{
|
||||
id: "painted",
|
||||
name: "Painted die",
|
||||
description:
|
||||
"One of the dice coloured using modern techniques that hide the attempt to load it.",
|
||||
// Inara: 20 / 6.7 / 6.7 / 6.7 / 40 / 20
|
||||
weights: [18.7, 6.2, 6.2, 6.2, 43.7, 18.7],
|
||||
},
|
||||
{
|
||||
id: "painters_b",
|
||||
name: "Painter's die B",
|
||||
description: "Painter Voyta's blue-painted die.",
|
||||
weights: [9.1, 27.2, 18.2, 18.2, 18.2, 9.1],
|
||||
},
|
||||
{
|
||||
id: "painters_g",
|
||||
name: "Painter's die G",
|
||||
description: "Painter Voyta's green-painted die.",
|
||||
weights: [9.1, 27.2, 18.2, 18.2, 18.2, 9.1],
|
||||
},
|
||||
{
|
||||
id: "painters_r",
|
||||
name: "Painter's die R",
|
||||
description: "Painter Voyta's red-painted die.",
|
||||
weights: [9.1, 27.2, 18.2, 18.2, 18.2, 9.1],
|
||||
},
|
||||
{
|
||||
id: "pie",
|
||||
name: "Pie die",
|
||||
description:
|
||||
"Doesn't look particularly tasty, but it's well balanced towards lower numbers.",
|
||||
weights: [46.2, 7.7, 23.1, 23.1, 0, 0],
|
||||
},
|
||||
{
|
||||
id: "premolar",
|
||||
name: "Premolar die",
|
||||
description: "A playing die made from a premolar tooth.",
|
||||
weights: [16.7, 16.7, 16.7, 16.7, 16.7, 16.7],
|
||||
},
|
||||
{
|
||||
id: "sad_greaser",
|
||||
name: "Sad Greaser's die",
|
||||
description: "A blue die mirroring the sadness of its original owner.",
|
||||
weights: [26.1, 26.1, 4.3, 4.3, 26.1, 13],
|
||||
},
|
||||
{
|
||||
id: "saint_antiochus",
|
||||
name: "Saint Antiochus' die",
|
||||
description:
|
||||
"The Saint Antiochus' die always rolls a 3. Not 2. And especially not 4.",
|
||||
weights: [20, 6.7, 40, 6.7, 6.7, 20],
|
||||
},
|
||||
{
|
||||
id: "shrinking",
|
||||
name: "Shrinking die",
|
||||
description:
|
||||
"A very lightly loaded die. One can barely differentiate it from an ordinary die.",
|
||||
weights: [22.2, 11.1, 11.1, 11.1, 11.1, 33.3],
|
||||
},
|
||||
{
|
||||
id: "st_stephens",
|
||||
name: "St. Stephen's die",
|
||||
description:
|
||||
"A die blessed by St. Stephen, guaranteeing favourable numbers in the game and protection from loose stones.",
|
||||
weights: [16.7, 16.7, 16.7, 16.7, 16.7, 16.7],
|
||||
},
|
||||
{
|
||||
id: "strip",
|
||||
name: "Strip die",
|
||||
description: "Legend has it this die will help you undress many a wench.",
|
||||
weights: [25, 12.5, 12.5, 12.5, 18.8, 18.8],
|
||||
},
|
||||
{
|
||||
id: "tengri",
|
||||
name: "Tengri's die",
|
||||
description:
|
||||
"This one appeared to me after I won on all of Tengri's tracks. A little token of my tremendous success, perhaps?",
|
||||
weights: [28.5, 14.3, 14.3, 14.3, 14.3, 14.3],
|
||||
},
|
||||
{
|
||||
id: "trinity",
|
||||
name: "Trinity die",
|
||||
description: "For some reason, this die usually rolls a three. Why?",
|
||||
// Inara: 18.2 / 9.1 / 36.4 / 9.1 / 18.2 / 9.1
|
||||
weights: [12.5, 6.2, 56.2, 6.2, 12.5, 6.2],
|
||||
},
|
||||
{
|
||||
id: "unbalanced",
|
||||
name: "Unbalanced die",
|
||||
description:
|
||||
"A playing die someone tried to load to his advantage, but didn't do a very good job.",
|
||||
weights: [25, 33.3, 8.3, 8.3, 16.7, 8.3],
|
||||
},
|
||||
{
|
||||
id: "unlucky",
|
||||
name: "Unlucky die",
|
||||
description:
|
||||
"Sometimes Lady Luck is on your side, sometimes she isn't. With this die, she most likely isn't.",
|
||||
weights: [9.1, 27.3, 18.2, 18.2, 18.2, 9.1],
|
||||
},
|
||||
{
|
||||
id: "wagoner",
|
||||
name: "Wagoner's die",
|
||||
description:
|
||||
"According to legend, this die belonged to the famous Roman charioteer Arnuldus, whose tactics consisted of tiring his opponents or lulling them to sleep.",
|
||||
weights: [5.6, 27.8, 33.3, 11.1, 11.1, 11.1],
|
||||
},
|
||||
{
|
||||
id: "weighted",
|
||||
name: "Weighted die",
|
||||
description:
|
||||
"A mysterious playing die found in a ruined house. Suspiciously, it tends to land on 1.",
|
||||
weights: [66.7, 6.7, 6.7, 6.7, 6.7, 6.7],
|
||||
},
|
||||
{
|
||||
id: "wisdom_tooth",
|
||||
name: "Wisdom tooth die",
|
||||
description: "A playing die made from a wisdom tooth.",
|
||||
weights: [16.7, 16.7, 16.7, 16.7, 16.7, 16.7],
|
||||
},
|
||||
];
|
||||
import { RAW_DICE } from "./rawDice.ts";
|
||||
|
||||
/** All dice, sorted by display name, with weights normalised to sum to 1. */
|
||||
export const DICE: readonly Die[] = RAW_DICE.map((raw) => ({
|
||||
|
||||
22
kcd2_dice_solver/src/data/headstart.ts
Normal file
22
kcd2_dice_solver/src/data/headstart.ts
Normal file
@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Headstart badge point leads.
|
||||
*
|
||||
* Its own module so both halves of the badge table can import it without
|
||||
* a cycle -- an earlier attempt had badgeTableGold importing it from
|
||||
* badgeTable, which left it undefined at module-init time.
|
||||
*/
|
||||
|
||||
import type { BadgeTier } from "./badges.ts";
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Point leads granted by the Headstart badges.
|
||||
*
|
||||
* UNVERIFIED. The wiki only says "small" / "moderate" / "large".
|
||||
*/
|
||||
export const HEADSTART_POINTS: Readonly<Record<BadgeTier, number>> = {
|
||||
tin: 250,
|
||||
silver: 500,
|
||||
gold: 1000,
|
||||
};
|
||||
21
kcd2_dice_solver/src/data/rawDice.ts
Normal file
21
kcd2_dice_solver/src/data/rawDice.ts
Normal file
@ -0,0 +1,21 @@
|
||||
/**
|
||||
* The raw die table: every die's unnormalised face weights.
|
||||
*
|
||||
* Split out of data/dice.ts to keep it under the 250-line cap. Pure data;
|
||||
* dice.ts still owns normalise() and the derived DICE/DICE_BY_ID exports.
|
||||
*/
|
||||
|
||||
import type { Face, Weights } from "./dice.ts";
|
||||
export interface RawDie {
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly description: string;
|
||||
readonly weights: Weights;
|
||||
readonly wildcardFaces?: readonly Face[];
|
||||
readonly wildScoresAlone?: boolean;
|
||||
}
|
||||
|
||||
import { RAW_DICE_A_M } from "./rawDiceAM.ts";
|
||||
import { RAW_DICE_N_Z } from "./rawDiceNZ.ts";
|
||||
|
||||
export const RAW_DICE: readonly RawDie[] = [...RAW_DICE_A_M, ...RAW_DICE_N_Z];
|
||||
178
kcd2_dice_solver/src/data/rawDiceAM.ts
Normal file
178
kcd2_dice_solver/src/data/rawDiceAM.ts
Normal file
@ -0,0 +1,178 @@
|
||||
/**
|
||||
* Raw die weights, first half by id.
|
||||
*
|
||||
* Split out of data/rawDice.ts to keep each file under the 250-line cap.
|
||||
* Pure data; rawDice.ts concatenates the halves back into RAW_DICE.
|
||||
*/
|
||||
|
||||
import type { RawDie } from "./rawDice.ts";
|
||||
|
||||
export const RAW_DICE_A_M: readonly RawDie[] = [
|
||||
{
|
||||
id: "aranka",
|
||||
name: "Aranka's die",
|
||||
description:
|
||||
"Aranka gave me this die to make it easier for me to play against her husband.",
|
||||
weights: [28.6, 4.8, 28.6, 4.8, 28.6, 4.8],
|
||||
},
|
||||
{
|
||||
id: "balatro",
|
||||
name: "Balatro's die",
|
||||
description:
|
||||
"A die crafted by the balatro Jimbo, marked with his grinning face. When it lands, you get to choose how it's counted!",
|
||||
// Only ONE face is the joker — Jimbo's grinning face, visible on the item
|
||||
// icon. The wiki's dice-effect table for this die is `{{Dice effect|d|d|d|d|
|
||||
// d|d}}`: six *unfilled placeholders* that render as "d%", not six wildcards.
|
||||
// Reading them as wildcards is what made this die look unbeatable.
|
||||
//
|
||||
// No source publishes its side weights (the wiki cells are the placeholders
|
||||
// above and Inara omits the die entirely), so uniform is an assumption, kept
|
||||
// as [1,1,1,1,1,1] rather than a fake "16.7" so it cannot be mistaken for a
|
||||
// transcribed figure.
|
||||
//
|
||||
// The joker replaces the one — confirmed by the player, who owns the game —
|
||||
// like the Devil's head die it is explicitly compared to. That is also why
|
||||
// holding it alone counts as a 1: the joker sits where the 1 used to be, so
|
||||
// the die has no plain single pip at all.
|
||||
weights: [1, 1, 1, 1, 1, 1],
|
||||
wildcardFaces: [1],
|
||||
// "When it lands, you get to choose how it's counted!" — and, unlike the
|
||||
// Devil's head, "picking it alone will count as if you threw 1".
|
||||
wildScoresAlone: true,
|
||||
},
|
||||
{
|
||||
id: "cautious_cheater",
|
||||
name: "Cautious cheater's die",
|
||||
description:
|
||||
"A die modified by an expert. It is precisely loaded, but also inconspicuous.",
|
||||
weights: [23.8, 14.3, 9.5, 14.3, 23.8, 14.3],
|
||||
},
|
||||
{
|
||||
id: "ci",
|
||||
name: "Ci die",
|
||||
description:
|
||||
"The second in the line of the demonic dice, she likes to get lost, but when she's with her sisters she's very strong.",
|
||||
// Inara: 14.3 / 14.3 / 14.3 / 14.3 / 14.3 / 28.6
|
||||
weights: [13, 13, 13, 13, 13, 34.8],
|
||||
},
|
||||
{
|
||||
id: "devils_head",
|
||||
name: "Devil's head die",
|
||||
description:
|
||||
"A die that feels hot to the touch. In place of a one it has a devil's head, which is not something folk like to gaze upon…",
|
||||
// Face 1 is the devil's head, which the wiki scoring table marks "Subst" —
|
||||
// it substitutes for any value, but per the die's own page it "acts as a
|
||||
// joker, matching any combination but never scoring on its own", so it
|
||||
// cannot be the lone 1 or 5 that lets you hold a die.
|
||||
weights: [16.7, 16.7, 16.7, 16.7, 16.7, 16.7],
|
||||
wildcardFaces: [1],
|
||||
},
|
||||
{
|
||||
id: "misfortune",
|
||||
name: "Die of misfortune",
|
||||
description:
|
||||
"They say that when it rains, it pours. But if you play with this die, the only thing pouring will be your tears.",
|
||||
weights: [4.5, 22.7, 22.7, 22.7, 22.7, 4.5],
|
||||
},
|
||||
{
|
||||
id: "even",
|
||||
name: "Even die",
|
||||
description: "A die loaded in favour of even numbers.",
|
||||
weights: [6.7, 26.7, 6.7, 26.7, 6.7, 26.7],
|
||||
},
|
||||
{
|
||||
id: "favourable",
|
||||
name: "Favourable die",
|
||||
description: "A playing die that brings luck more often than you'd expect.",
|
||||
weights: [33.3, 0, 5.6, 5.6, 33.3, 22.2],
|
||||
},
|
||||
{
|
||||
id: "fer",
|
||||
name: "Fer die",
|
||||
description: "The third and last in the line of demonic dice.",
|
||||
// Inara: 14.3 / 14.3 / 14.3 / 14.3 / 14.3 / 28.6
|
||||
weights: [13, 13, 13, 13, 13, 34.8],
|
||||
},
|
||||
{
|
||||
id: "greasy",
|
||||
name: "Greasy die",
|
||||
description:
|
||||
"A more reliable die than a normal one, but it cannot be relied on for everything.",
|
||||
weights: [17.6, 11.8, 17.6, 11.7, 17.6, 23.5],
|
||||
},
|
||||
{
|
||||
id: "grimy",
|
||||
name: "Grimy die",
|
||||
description:
|
||||
"One could say it will get you out of the frying pan into the fire. And sometimes it will let you stew in your own juice.",
|
||||
weights: [6.2, 31.2, 6.2, 6.2, 43.7, 6.2],
|
||||
},
|
||||
{
|
||||
id: "grozav",
|
||||
name: "Grozav's lucky die",
|
||||
description: "It is actually not all that lucky, but don't tell anyone!",
|
||||
weights: [6.7, 66.7, 6.7, 6.7, 6.7, 6.7],
|
||||
},
|
||||
{
|
||||
id: "heavenly_kingdom",
|
||||
name: "Heavenly Kingdom die",
|
||||
description:
|
||||
"A miraculous playing die, sent from the Heavenly Kingdom to the kingdom of men.",
|
||||
weights: [36.8, 10.5, 10.5, 10.5, 10.5, 21],
|
||||
},
|
||||
{
|
||||
id: "holy_trinity",
|
||||
name: "Holy Trinity die",
|
||||
description:
|
||||
"A consecrated die commemorating the Holy Trinity, especially by rolling threes.",
|
||||
// Inara: 21.1 / 26.3 / 36.8 / 5.3 / 5.3 / 5.3
|
||||
weights: [18.2, 22.7, 45.4, 4.5, 4.5, 4.5],
|
||||
},
|
||||
{
|
||||
id: "hugo",
|
||||
name: "Hugo's die",
|
||||
description: "A die of the most loyal regular at the Hole. It bears his likeness.",
|
||||
weights: [16.7, 16.7, 16.7, 16.7, 16.7, 16.7],
|
||||
},
|
||||
{
|
||||
id: "kings",
|
||||
name: "King's die",
|
||||
description:
|
||||
"An anointed head has no need of a loaded die, since he is always right and can never lose.",
|
||||
weights: [12.5, 18.7, 21.9, 25, 12.5, 9.4],
|
||||
},
|
||||
{
|
||||
id: "lousy_gambler",
|
||||
name: "Lousy gambler's die",
|
||||
description: "A shoddy loaded die. It's quite noticeably unbalanced.",
|
||||
weights: [10, 15, 10, 15, 35, 15],
|
||||
},
|
||||
{
|
||||
id: "lu",
|
||||
name: "Lu die",
|
||||
description: "The first of the line of demonic dice.",
|
||||
// Inara: 14.3 / 14.3 / 14.3 / 14.3 / 14.3 / 28.6
|
||||
weights: [13, 13, 13, 13, 13, 34.8],
|
||||
},
|
||||
{
|
||||
id: "lucky",
|
||||
name: "Lucky die",
|
||||
description:
|
||||
"When fortune smiles on you, smile back. Otherwise you'll look suspicious.",
|
||||
weights: [27.3, 4.5, 9.1, 13.6, 18.2, 27.3],
|
||||
},
|
||||
{
|
||||
id: "mathematician",
|
||||
name: "Mathematician's die",
|
||||
description:
|
||||
"A die loaded based on the work of a forgotten mathematician. It may be better suited to solving equations than playing dice.",
|
||||
weights: [16.7, 20.8, 25, 29.2, 4.2, 4.2],
|
||||
},
|
||||
{
|
||||
id: "molar",
|
||||
name: "Molar die",
|
||||
description:
|
||||
"A die made out of a molar tooth. It's probably better not to know who it came from.",
|
||||
weights: [16.7, 16.7, 16.7, 16.7, 16.7, 16.7],
|
||||
},
|
||||
];
|
||||
157
kcd2_dice_solver/src/data/rawDiceNZ.ts
Normal file
157
kcd2_dice_solver/src/data/rawDiceNZ.ts
Normal file
@ -0,0 +1,157 @@
|
||||
/**
|
||||
* Raw die weights, second half by id.
|
||||
*
|
||||
* Split out of data/rawDice.ts to keep each file under the 250-line cap.
|
||||
* Pure data; rawDice.ts concatenates the halves back into RAW_DICE.
|
||||
*/
|
||||
|
||||
import type { RawDie } from "./rawDice.ts";
|
||||
|
||||
export const RAW_DICE_N_Z: readonly RawDie[] = [
|
||||
{
|
||||
id: "monk",
|
||||
name: "Monk's die",
|
||||
description:
|
||||
"No one knows for sure whether it was blessed by a holy man or just spat on by a drunken monk. Either way, the die rolls... sort of.",
|
||||
weights: [40, 40, 5, 5, 5, 5],
|
||||
},
|
||||
{
|
||||
id: "mother_of_pearl",
|
||||
name: "Mother-of-pearl die",
|
||||
description:
|
||||
"A rare pink mother-of-pearl die. Said to bring luck in games, but misfortune in love.",
|
||||
weights: [25, 8.3, 8.3, 8.3, 25, 25],
|
||||
},
|
||||
{
|
||||
id: "odd",
|
||||
name: "Odd die",
|
||||
description: "A die loaded to favour odd numbers.",
|
||||
weights: [26.7, 6.7, 26.7, 6.7, 26.7, 6.7],
|
||||
},
|
||||
{
|
||||
id: "ordinary",
|
||||
name: "Ordinary die",
|
||||
description: "An ordinary playing die.",
|
||||
weights: [16.7, 16.7, 16.7, 16.7, 16.7, 16.7],
|
||||
},
|
||||
{
|
||||
id: "painted",
|
||||
name: "Painted die",
|
||||
description:
|
||||
"One of the dice coloured using modern techniques that hide the attempt to load it.",
|
||||
// Inara: 20 / 6.7 / 6.7 / 6.7 / 40 / 20
|
||||
weights: [18.7, 6.2, 6.2, 6.2, 43.7, 18.7],
|
||||
},
|
||||
{
|
||||
id: "painters_b",
|
||||
name: "Painter's die B",
|
||||
description: "Painter Voyta's blue-painted die.",
|
||||
weights: [9.1, 27.2, 18.2, 18.2, 18.2, 9.1],
|
||||
},
|
||||
{
|
||||
id: "painters_g",
|
||||
name: "Painter's die G",
|
||||
description: "Painter Voyta's green-painted die.",
|
||||
weights: [9.1, 27.2, 18.2, 18.2, 18.2, 9.1],
|
||||
},
|
||||
{
|
||||
id: "painters_r",
|
||||
name: "Painter's die R",
|
||||
description: "Painter Voyta's red-painted die.",
|
||||
weights: [9.1, 27.2, 18.2, 18.2, 18.2, 9.1],
|
||||
},
|
||||
{
|
||||
id: "pie",
|
||||
name: "Pie die",
|
||||
description:
|
||||
"Doesn't look particularly tasty, but it's well balanced towards lower numbers.",
|
||||
weights: [46.2, 7.7, 23.1, 23.1, 0, 0],
|
||||
},
|
||||
{
|
||||
id: "premolar",
|
||||
name: "Premolar die",
|
||||
description: "A playing die made from a premolar tooth.",
|
||||
weights: [16.7, 16.7, 16.7, 16.7, 16.7, 16.7],
|
||||
},
|
||||
{
|
||||
id: "sad_greaser",
|
||||
name: "Sad Greaser's die",
|
||||
description: "A blue die mirroring the sadness of its original owner.",
|
||||
weights: [26.1, 26.1, 4.3, 4.3, 26.1, 13],
|
||||
},
|
||||
{
|
||||
id: "saint_antiochus",
|
||||
name: "Saint Antiochus' die",
|
||||
description:
|
||||
"The Saint Antiochus' die always rolls a 3. Not 2. And especially not 4.",
|
||||
weights: [20, 6.7, 40, 6.7, 6.7, 20],
|
||||
},
|
||||
{
|
||||
id: "shrinking",
|
||||
name: "Shrinking die",
|
||||
description:
|
||||
"A very lightly loaded die. One can barely differentiate it from an ordinary die.",
|
||||
weights: [22.2, 11.1, 11.1, 11.1, 11.1, 33.3],
|
||||
},
|
||||
{
|
||||
id: "st_stephens",
|
||||
name: "St. Stephen's die",
|
||||
description:
|
||||
"A die blessed by St. Stephen, guaranteeing favourable numbers in the game and protection from loose stones.",
|
||||
weights: [16.7, 16.7, 16.7, 16.7, 16.7, 16.7],
|
||||
},
|
||||
{
|
||||
id: "strip",
|
||||
name: "Strip die",
|
||||
description: "Legend has it this die will help you undress many a wench.",
|
||||
weights: [25, 12.5, 12.5, 12.5, 18.8, 18.8],
|
||||
},
|
||||
{
|
||||
id: "tengri",
|
||||
name: "Tengri's die",
|
||||
description:
|
||||
"This one appeared to me after I won on all of Tengri's tracks. A little token of my tremendous success, perhaps?",
|
||||
weights: [28.5, 14.3, 14.3, 14.3, 14.3, 14.3],
|
||||
},
|
||||
{
|
||||
id: "trinity",
|
||||
name: "Trinity die",
|
||||
description: "For some reason, this die usually rolls a three. Why?",
|
||||
// Inara: 18.2 / 9.1 / 36.4 / 9.1 / 18.2 / 9.1
|
||||
weights: [12.5, 6.2, 56.2, 6.2, 12.5, 6.2],
|
||||
},
|
||||
{
|
||||
id: "unbalanced",
|
||||
name: "Unbalanced die",
|
||||
description:
|
||||
"A playing die someone tried to load to his advantage, but didn't do a very good job.",
|
||||
weights: [25, 33.3, 8.3, 8.3, 16.7, 8.3],
|
||||
},
|
||||
{
|
||||
id: "unlucky",
|
||||
name: "Unlucky die",
|
||||
description:
|
||||
"Sometimes Lady Luck is on your side, sometimes she isn't. With this die, she most likely isn't.",
|
||||
weights: [9.1, 27.3, 18.2, 18.2, 18.2, 9.1],
|
||||
},
|
||||
{
|
||||
id: "wagoner",
|
||||
name: "Wagoner's die",
|
||||
description:
|
||||
"According to legend, this die belonged to the famous Roman charioteer Arnuldus, whose tactics consisted of tiring his opponents or lulling them to sleep.",
|
||||
weights: [5.6, 27.8, 33.3, 11.1, 11.1, 11.1],
|
||||
},
|
||||
{
|
||||
id: "weighted",
|
||||
name: "Weighted die",
|
||||
description:
|
||||
"A mysterious playing die found in a ruined house. Suspiciously, it tends to land on 1.",
|
||||
weights: [66.7, 6.7, 6.7, 6.7, 6.7, 6.7],
|
||||
},
|
||||
{
|
||||
id: "wisdom_tooth",
|
||||
name: "Wisdom tooth die",
|
||||
description: "A playing die made from a wisdom tooth.",
|
||||
weights: [16.7, 16.7, 16.7, 16.7, 16.7, 16.7],
|
||||
},
|
||||
];
|
||||
Loading…
Reference in New Issue
Block a user