Dice

name

Dice

icon

casino

description

Simple, Advanced, Specialized Dice Randomizer.
Open Random Notes to review or revisit based on different Note Properties (In Basic Option).
Randomize any Table in Amplenote.
Have some of your favorite games rolled here at Amplenote.

instructions

Run the Plugin: Use the Note Options menu (for Table - Randomizer) or run any dice roller as a / command (Insert Text option).

In-Context Results: Roll results are inserted directly at your cursor location (or at the top of the note for Table Randomizer).

Remembered Inputs: At first run, you will see blank fields to fill up. In subsequent rolls, the plugin will remember your previous settings.

Custom Choices: Play around with the extensive list of dice limits, RPG stunt points, card layouts, and generators to customize your results.

Look Up Notes: Under the Basic option, use the Look Up in your Notes dropdown to find and open a random note matching your specified properties.

Audit Logging: Every roll is logged into a centralized Dice Results Audit note.
Navigation is bypassed so you can keep writing, but you can view your log note anytime using the View Roll History command.

Note: More options are on its way. Comment in the Installation page for any feedback or suggestion.
GitHub Link: https://github.com/krishnakanthb13/anp-19-dice
YouTube: https://youtu.be/f3BFc32RxJ4

setting

Previous_Roll

setting

Previous_Roll_Spc

setting

Previous_Roll_FF

setting

Previous_Roll_AGE

setting

Previous_Roll_Ran

setting

Previous_Weighted

setting

Previous_DicePool

setting

Dice_Audit_UUID [Do not Edit!]

setting

Dice_Show_Alert

(() => {
// anp-19-dice/lib/utils.js
function rollDice({
numDice = null,
faces = null,
min = null,
max = null,
keep = null,
drop = null,
explode = null,
sort = null,
unique = false
} = {}) {
const rollSingleDie = () => Math.floor(Math.random() * faces) + 1;
let rolls = Array.from({ length: numDice }, rollSingleDie);
if (min !== null) rolls = rolls.map((roll) => Math.max(roll, min));
if (max !== null) rolls = rolls.map((roll) => Math.min(roll, max));
if (explode) {
const { target, reroll } = explode;
const newRolls = [];
rolls.forEach((roll) => {
newRolls.push(roll);
let explodeCount = 0;
let currentRoll = roll;
while (currentRoll === target && explodeCount < 100) {
const extraRoll = rollSingleDie();
newRolls.push(extraRoll);
currentRoll = extraRoll;
explodeCount++;
if (!reroll) break;
}
});
rolls = newRolls;
}
if (unique) rolls = [...new Set(rolls)];
if (keep || drop) {
rolls.sort((a, b) => a - b);
if (keep) {
const { highest, count } = keep;
rolls = highest ? rolls.slice(-count) : rolls.slice(0, count);
}
if (drop) {
const { highest, count } = drop;
rolls = highest ? rolls.slice(0, rolls.length - count) : rolls.slice(count);
}
}
if (sort === "asc") rolls.sort((a, b) => a - b);
if (sort === "desc") rolls.sort((a, b) => b - a);
return {
rolls,
total: rolls.reduce((sum, roll) => sum + roll, 0)
};
}
async function sortNotesByLookUp(app, lookUp, pickNote, preFetchedNotes = null) {
let notesByGroup = preFetchedNotes;
if (!notesByGroup) {
try {
notesByGroup = await app.filterNotes({});
if (!notesByGroup || !Array.isArray(notesByGroup)) {
notesByGroup = [];
}
} catch (error) {
console.error("Error fetching notes:", error);
notesByGroup = [];
return null;
}
}
notesByGroup = notesByGroup.filter((note) => note !== null && note !== void 0);
switch (lookUp) {
case 1:
notesByGroup.sort((a, b) => (a.name || "").localeCompare(b.name || ""));
break;
case 2:
notesByGroup.sort((a, b) => new Date(a.created || 0) - new Date(b.created || 0));
break;
case 3:
notesByGroup.sort((a, b) => new Date(a.updated || 0) - new Date(b.updated || 0));
break;
case 4:
notesByGroup = shuffleArray(notesByGroup);
break;
case 6:
notesByGroup.sort((a, b) => (a.uuid || "").localeCompare(b.uuid || ""));
break;
case 7:
notesByGroup.sort((a, b) => {
const aTag = a.tags && a.tags.length > 0 ? a.tags[0].toLowerCase() : "";
const bTag = b.tags && b.tags.length > 0 ? b.tags[0].toLowerCase() : "";
if (aTag !== bTag) {
return aTag.localeCompare(bTag);
}
return (a.name || "").localeCompare(b.name || "");
});
break;
case 5:
return null;
default:
notesByGroup.sort((a, b) => (a.name || "").localeCompare(b.name || ""));
}
const totalNotes = notesByGroup.length;
if (totalNotes === 0) {
console.log("No valid notes available to pick.");
return null;
}
const pickNumber = typeof pickNote === "number" ? pickNote : 0;
const adjustedPickNote = (pickNumber % totalNotes + totalNotes) % totalNotes;
const selectedNote = notesByGroup[adjustedPickNote];
return selectedNote && selectedNote.uuid ? selectedNote.uuid : null;
function shuffleArray(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array;
}
}
async function getNoteUUID(app, noteName, tagNames) {
const existingUUID = await app.settings["Dice_Audit_UUID [Do not Edit!]"];
if (existingUUID) {
if (existingUUID.startsWith("local-")) {
try {
let allNotes = await app.filterNotes({});
if (allNotes && Array.isArray(allNotes)) {
const matchingNotes = allNotes.filter((note) => {
const nameMatches = note.name === noteName;
const tagMatches = note.tags && note.tags.length > 0 && tagNames.every((tag) => note.tags.includes(tag));
return nameMatches && tagMatches;
});
const onlineNote = matchingNotes.find(
(note) => note.uuid && !note.uuid.startsWith("local-")
);
if (onlineNote && onlineNote.uuid) {
await app.setSetting("Dice_Audit_UUID [Do not Edit!]", onlineNote.uuid);
return onlineNote.uuid;
}
if (matchingNotes.length > 0 && matchingNotes[0].uuid) {
return matchingNotes[0].uuid;
}
}
try {
const localNote = await app.getNote({ uuid: existingUUID });
if (localNote) {
return existingUUID;
}
} catch (localError) {
console.error("Error fetching local note:", localError);
}
} catch (error) {
console.error("Error resolving UUID:", error);
}
} else {
return existingUUID;
}
}
try {
const newUUID = await app.createNote(noteName, tagNames);
await app.setSetting("Dice_Audit_UUID [Do not Edit!]", newUUID);
return newUUID;
} catch (error) {
console.error("Error creating note:", error);
throw error;
}
}
function stripMarkdown(text) {
if (typeof text !== "string") return text;
let clean = text;
clean = clean.replace(/<\/?mark>/gi, "");
clean = clean.replace(/\[Report\]\[\^[^\]]+\]\s*/g, "");
clean = clean.replace(/\[\^[^\]]+\]:\s*(?:\[\]\(\)\s*)?/g, "");
clean = clean.replace(/^#{1,6}\s+(.*)$/gm, "$1:");
clean = clean.replace(/(\*\*\*|___)(.*?)\1/g, "$2");
clean = clean.replace(/(\*\*|__)(.*?)\2/g, "$2");
clean = clean.replace(/(\*|_)(.*?)\1/g, "$2");
clean = clean.replace(/`([^`]+)`/g, "$1");
const lines = clean.split("\n");
const processedLines = [];
for (let line of lines) {
const trimmed = line.trim();
if (trimmed.startsWith("|") && trimmed.endsWith("|") && /^[|\s:-]+$/.test(trimmed)) {
continue;
}
if (trimmed.startsWith("|") && trimmed.endsWith("|")) {
const cells = line.split("|").map((c) => c.trim()).filter((_, idx, arr) => idx > 0 && idx < arr.length - 1);
if (cells.length === 2) {
processedLines.push(`${cells[0]}: ${cells[1]}`);
} else if (cells.length > 0) {
processedLines.push(cells.join(" \u2022 "));
}
continue;
}
if (trimmed.startsWith(".-") || trimmed.startsWith("- ")) {
processedLines.push("\u2022 " + trimmed.replace(/^(\.-|-)\s*/, ""));
continue;
}
processedLines.push(line);
}
return processedLines.join("\n").replace(/\n{3,}/g, "\n\n").trim();
}
 
// anp-19-dice/lib/basic.js
async function basic_default(app) {
const existingSetting = await app.settings["Previous_Roll"];
let result;
if (existingSetting) {
let parsed = (existingSetting || "").split(",").map((value) => value === "" || value === "null" ? null : value);
if (parsed.length === 13) {
parsed.splice(12, 0, "false");
}
const [
numDicez,
facesz,
minz,
maxz,
keepHighestz,
keepCountz,
dropHighestz,
dropCountz,
explodez,
explodeTargetz,
sortOptionz,
uniquez,
navigateToNotez,
lookUpz
] = parsed.map((value, index) => {
const defaults = [1, 6, null, null, false, 0, false, 0, false, 0, 1, false, false, 5];
if (value === void 0 || value === null) {
return defaults[index];
}
if ([0, 1, 2, 3, 5, 7, 9, 10, 13].includes(index)) return Number(value) || defaults[index];
if ([4, 6, 8, 11, 12].includes(index)) return String(value).toLowerCase() === "true";
return value;
});
result = await app.prompt("Configure Your Dice Roll (Recall settings)", {
inputs: [
{ label: "Number of Dice", type: "string", value: numDicez },
{ label: "Number of Faces", type: "string", value: facesz },
{ label: "Minimum Roll Limit", type: "string", value: minz },
{ label: "Maximum Roll Limit", type: "string", value: maxz },
{ label: "Keep Highest Rolls (Discard the rest)", type: "checkbox", value: keepHighestz },
{ label: "Count of Highest Rolls to Keep", type: "string", value: keepCountz },
{ label: "Drop Highest Rolls (Keep the rest)", type: "checkbox", value: dropHighestz },
{ label: "Count of Highest Rolls to Drop", type: "string", value: dropCountz },
{ label: "Exploding Dice (Roll again on max value)", type: "checkbox", value: explodez },
{ label: "Explode Target Value", type: "string", value: explodeTargetz },
{ label: "Sort Results", type: "select", options: [{ label: "None", value: 1 }, { label: "Ascending", value: 2 }, { label: "Descending", value: 3 }], value: sortOptionz || 1 },
{ label: "Unique Results Only (No duplicates)", type: "checkbox", value: uniquez },
{ label: "Open Retrieved Note Automatically", type: "checkbox", value: navigateToNotez },
{ label: "Lookup Note (Order by)", type: "select", options: [{ label: "None", value: 5 }, { label: "Name", value: 1 }, { label: "Created", value: 2 }, { label: "Modified", value: 3 }, { label: "UUID", value: 6 }, { label: "Tags", value: 7 }, { label: "Random", value: 4 }], value: lookUpz || 5 }
]
});
} else {
result = await app.prompt("Configure Your Dice Roll", {
inputs: [
{ label: "Number of Dice", type: "string" },
{ label: "Number of Faces", type: "string" },
{ label: "Minimum Roll Limit", type: "string" },
{ label: "Maximum Roll Limit", type: "string" },
{ label: "Keep Highest Rolls (Discard the rest)", type: "checkbox" },
{ label: "Count of Highest Rolls to Keep", type: "string" },
{ label: "Drop Highest Rolls (Keep the rest)", type: "checkbox" },
{ label: "Count of Highest Rolls to Drop", type: "string" },
{ label: "Exploding Dice (Roll again on max value)", type: "checkbox" },
{ label: "Explode Target Value", type: "string" },
{ label: "Sort Results", type: "select", options: [{ label: "None", value: 1 }, { label: "Ascending", value: 2 }, { label: "Descending", value: 3 }], value: 1 },
{ label: "Unique Results Only (No duplicates)", type: "checkbox" },
{ label: "Open Retrieved Note Automatically", type: "checkbox", value: false },
{ label: "Lookup Note (Order by)", type: "select", options: [{ label: "None", value: 5 }, { label: "Name", value: 1 }, { label: "Created", value: 2 }, { label: "Modified", value: 3 }, { label: "UUID", value: 6 }, { label: "Tags", value: 7 }, { label: "Random", value: 4 }], value: 5 }
]
});
}
if (result) {
const [
numDice,
faces,
min,
max,
keepHighest,
keepCount,
dropHighest,
dropCount,
explode,
explodeTarget,
sortOption,
unique,
navigateToNote,
lookUp
] = result;
const numDiceNum = Number(numDice);
const facesNum = Number(faces);
if (numDiceNum < 1 || facesNum < 1) {
app.alert("Number of dice and faces must be at least 1");
return;
}
const resultx = `**NumDice**: ${numDice},
**Faces**: ${faces},
**Min**: ${min},
**Max**: ${max},
**Keep Highest**: ${keepHighest},
**Keep Count**: ${keepCount},
**Drop Highest**: ${dropHighest},
**Drop Count**: ${dropCount},
**Explode**: ${explode},
**Explode Target**: ${explodeTarget},
**Sort Option**: ${sortOption},
**Unique**: ${unique},
**Navigate To Note**: ${navigateToNote},
**LookUp**: ${lookUp},`;
const finalResultx = `[Report][^ADV]
[^ADV]: []()${resultx}
`;
await app.setSetting("Previous_Roll", result);
const sortMap = { 1: null, 2: "asc", 3: "desc" };
const diceResult = rollDice({
numDice: Number(numDice) || 1,
faces: Number(faces) || 6,
min: min ? Number(min) : null,
max: max ? Number(max) : null,
keep: keepHighest ? { highest: true, count: Number(keepCount) || 1 } : null,
drop: dropHighest ? { highest: true, count: Number(dropCount) || 1 } : null,
explode: explode ? { target: Number(explodeTarget) || 6, reroll: true } : null,
sort: sortMap[sortOption],
unique: !!unique
});
const pickNote = diceResult.total;
const now = /* @__PURE__ */ new Date();
const YYMMDD = now.toISOString().slice(2, 10).replace(/-/g, "");
const HHMMSS = now.toTimeString().slice(0, 8).replace(/:/g, "");
const auditNoteName = `Dice Results Audit`;
const auditTagName = ["-reports/-dice"];
const auditnoteUUID = await getNoteUUID(app, auditNoteName, auditTagName);
if ([1, 2, 3, 4, 6, 7].includes(lookUp)) {
let preFetchedNotes = null;
try {
preFetchedNotes = await app.filterNotes({});
if (!preFetchedNotes || !Array.isArray(preFetchedNotes) || preFetchedNotes.length === 0) {
app.alert("No notes found in your account. Please create some notes first.");
const auditReport = `- <mark>Basic:</mark> ***When:** ${YYMMDD}_${HHMMSS}*; <mark>**Dice rolled:** ${diceResult.rolls}; **Total:** ${diceResult.total};</mark> **No notes found!**; **Options:** ${finalResultx}`;
await app.insertNoteContent({ uuid: auditnoteUUID }, auditReport);
await app.navigate(`https://www.amplenote.com/notes/${auditnoteUUID}`);
return;
}
} catch (error) {
console.error("Error checking notes:", error);
}
(async () => {
try {
const uuid = await sortNotesByLookUp(app, lookUp, pickNote, preFetchedNotes);
if (uuid && typeof uuid === "string" && uuid.trim() !== "") {
const auditReport = `- <mark>Basic:</mark> ***When:** ${YYMMDD}_${HHMMSS}*; <mark>**Dice rolled:** ${diceResult.rolls}; **Total:** ${diceResult.total};</mark> **UUID:** ${uuid}; **Options:** ${finalResultx}`;
await app.insertNoteContent({ uuid: auditnoteUUID }, auditReport);
if (navigateToNote) {
await app.navigate(`https://www.amplenote.com/notes/${uuid}`);
} else {
await app.navigate(`https://www.amplenote.com/notes/${auditnoteUUID}`);
}
} else {
const auditReport = `- <mark>Basic:</mark> ***When:** ${YYMMDD}_${HHMMSS}*; <mark>**Dice rolled:** ${diceResult.rolls}; **Total:** ${diceResult.total};</mark> **Note not found!**; **Options:** ${finalResultx}`;
await app.insertNoteContent({ uuid: auditnoteUUID }, auditReport);
await app.navigate(`https://www.amplenote.com/notes/${auditnoteUUID}`);
}
} catch (error) {
console.error(error.message);
const auditReport = `- <mark>Basic:</mark> ***When:** ${YYMMDD}_${HHMMSS}*; <mark>**Dice rolled:** ${diceResult.rolls}; **Total:** ${diceResult.total};</mark> **Error:** ${error.message}; **Options:** ${finalResultx}`;
await app.insertNoteContent({ uuid: auditnoteUUID }, auditReport);
await app.navigate(`https://www.amplenote.com/notes/${auditnoteUUID}`);
}
})();
} else {
(async () => {
try {
const auditReport = `- <mark>Basic:</mark> ***When:** ${YYMMDD}_${HHMMSS}*; <mark>**Dice rolled:** ${diceResult.rolls}; **Total:** ${diceResult.total};</mark> **Options:** ${finalResultx}`;
await app.insertNoteContent({ uuid: auditnoteUUID }, auditReport);
await app.navigate(`https://www.amplenote.com/notes/${auditnoteUUID}`);
} catch (error) {
console.error(error.message);
}
})();
}
}
}
 
// anp-19-dice/lib/advanced.js
async function advanced_default(app) {
class DiceParser {
/**
* Creates a parser for dice expressions.
* @returns {DiceParser} - Parser instance with reset input state.
*/
constructor() {
this.pos = 0;
this.input = "";
}
/**
* Rolls one die with the requested number of sides.
* @param {number} sides - Number of sides on the die.
* @returns {number} - Random value between 1 and sides.
*/
rollDie(sides) {
return Math.floor(Math.random() * sides) + 1;
}
/**
* Rolls multiple dice and returns their sum.
* @param {number} count - Number of dice to roll.
* @param {number} sides - Number of sides on each die.
* @returns {number} - Sum of all dice.
*/
rollDice(count, sides) {
let sum = 0;
for (let i = 0; i < count; i++) {
sum += this.rollDie(sides);
}
return sum;
}
/**
* Advances the parser past whitespace characters.
* @returns {void}
*/
skipWhitespace() {
while (this.pos < this.input.length && /\s/.test(this.input[this.pos])) {
this.pos++;
}
}
/**
* Parses either a literal number or dice notation such as 2d6.
* @returns {number} - Parsed literal or rolled dice total.
*/
parseNumber() {
this.skipWhitespace();
if (/\d/.test(this.input[this.pos])) {
let start = this.pos;
while (this.pos < this.input.length && /\d/.test(this.input[this.pos])) {
this.pos++;
}
if (this.pos < this.input.length && this.input[this.pos] === "d") {
const count = parseInt(this.input.slice(start, this.pos));
this.pos++;
start = this.pos;
while (this.pos < this.input.length && /\d/.test(this.input[this.pos])) {
this.pos++;
}
const sides = parseInt(this.input.slice(start, this.pos));
return this.rollDice(count, sides);
} else {
return parseInt(this.input.slice(start, this.pos));
}
}
throw new Error("Invalid number or dice expression");
}
/**
* Parses a parenthesized expression or a number-like term.
* @returns {number} - Parsed expression value.
*/
parseParentheses() {
this.skipWhitespace();
if (this.input[this.pos] === "(") {
this.pos++;
const result = this.parseExpression();
this.skipWhitespace();
if (this.input[this.pos] !== ")") {
throw new Error("Missing closing parenthesis");
}
this.pos++;
return result;
}
return this.parseNumber();
}
/**
* Parses exponent operations.
* @returns {number} - Parsed exponent expression value.
*/
parseExponent() {
let left = this.parseParentheses();
this.skipWhitespace();
while (this.pos < this.input.length && this.input[this.pos] === "^") {
this.pos++;
const right = this.parseParentheses();
left = Math.pow(left, right);
this.skipWhitespace();
}
return left;
}
/**
* Parses multiplication and division operations.
* @returns {number} - Parsed multiplication/division expression value.
*/
parseMultiplyDivide() {
let left = this.parseExponent();
this.skipWhitespace();
while (this.pos < this.input.length && (this.input[this.pos] === "*" || this.input[this.pos] === "/")) {
const operator = this.input[this.pos];
this.pos++;
const right = this.parseExponent();
if (operator === "*") {
left *= right;
} else {
left /= right;
}
this.skipWhitespace();
}
return left;
}
/**
* Parses addition and subtraction operations.
* @returns {number} - Parsed expression value.
*/
parseExpression() {
let left = this.parseMultiplyDivide();
this.skipWhitespace();
while (this.pos < this.input.length && (this.input[this.pos] === "+" || this.input[this.pos] === "-")) {
const operator = this.input[this.pos];
this.pos++;
const right = this.parseMultiplyDivide();
if (operator === "+") {
left += right;
} else {
left -= right;
}
this.skipWhitespace();
}
return left;
}
/**
* Parses a complete dice expression.
* @param {string} input - Dice expression to parse.
* @returns {number} - Evaluated expression value.
*/
parse(input) {
this.input = input.replace(/\s+/g, "").toLowerCase();
this.pos = 0;
const result = this.parseExpression();
if (this.pos < this.input.length) {
throw new Error("Invalid expression");
}
return result;
}
}
function evaluateDiceExpression(expression) {
const parser = new DiceParser();
try {
return parser.parse(expression);
} catch (error) {
return `Error: ${error.message}`;
}
}
async function main() {
const examplez = `1d2,
3d4 + 3,
1d12 + 1d10 + 5,
3d4+3d4-(3d4 * 1d4) - 2^1d7`;
const result = await app.prompt("Evaluate Custom Dice Formulas", {
inputs: [
{ label: "Single Roll Formula (e.g., 2d6 + 5)", type: "string", value: `1d2` },
{ label: "Batch Roll Formulas (One per line or comma-separated)", type: "text", value: `${examplez}` }
]
});
if (result) {
const [singleDice, multipleDice] = result;
let finalResult = ``;
if (singleDice) {
finalResult += `<mark>-- **Expression:** ${singleDice} --</mark>
`;
finalResult += `**Result:** ${evaluateDiceExpression(singleDice)}
`;
}
if (multipleDice) {
const multipleDicez = multipleDice.split(/[\n,]/).map((dice) => dice.trim()).filter((dice) => dice !== "");
for (let i = 0; i < multipleDicez.length; i++) {
const dice = multipleDicez[i];
finalResult += `<mark>-- **Expression:** ${dice} --</mark>
`;
finalResult += `**Result:** ${evaluateDiceExpression(dice)}
`;
}
}
const now = /* @__PURE__ */ new Date();
const YYMMDD = now.toISOString().slice(2, 10).replace(/-/g, "");
const HHMMSS = now.toTimeString().slice(0, 8).replace(/:/g, "");
const auditNoteName = `Dice Results Audit`;
const auditTagName = ["-reports/-dice"];
const auditnoteUUID = await getNoteUUID(app, auditNoteName, auditTagName);
const finalResultz = `[Report][^ADV]
[^ADV]: []()${finalResult}
`;
(async () => {
try {
const auditReport = `- <mark>Advanced:</mark> ***When:** ${YYMMDD}_${HHMMSS}*; <mark>**Dice rolled Report:**</mark> ${finalResultz}`;
await app.insertNoteContent({ uuid: auditnoteUUID }, auditReport);
await app.navigate(`https://www.amplenote.com/notes/${auditnoteUUID}`);
} catch (error) {
console.error(error.message);
}
})();
}
}
main();
}
 
// anp-19-dice/lib/specialized.js
async function specialized_default(app) {
const existingSetting = await app.settings["Previous_Roll_Spc"];
const [
numDicez,
specializedDicez,
pokerVariz,
addProbz
] = (existingSetting || "").split(",");
const result = await app.prompt("Configure Specialized Dice Roll", {
inputs: [
{ label: "Number of Dice", type: "string", value: numDicez || 5 },
{ label: "Specialized Dice Type", type: "select", options: [{ label: "Sicherman Dice", value: "sicherman" }, { label: "Intransitive Dice", value: "intransitive" }, { label: "Poker Dice", value: "poker" }], value: specializedDicez || "poker" },
{ label: "Poker Variation (If Poker selected)", type: "select", options: [{ label: "Standard", value: "standard" }, { label: "Numeric", value: "numeric" }, { label: "Crown", value: "crown" }], value: pokerVariz || "standard" },
{ label: "Calculate & Show Probabilities", type: "checkbox", value: addProbz || false }
]
});
const [
numDice,
specializedDice,
pokerVari,
addProb
] = result;
await app.setSetting("Previous_Roll_Spc", result);
const DICE_VARIATIONS = {
sicherman: {
die1: [1, 3, 4, 5, 6, 8],
die2: [1, 2, 2, 3, 3, 4]
},
intransitive: {
dieA: [2, 2, 4, 4, 9, 9],
dieB: [1, 1, 6, 6, 8, 8],
dieC: [3, 3, 5, 5, 7, 7]
},
poker: {
standard: ["A", "K", "Q", "J", "10", "9"],
numeric: [1, 2, 3, 4, 5, 6],
crown: ["Crown", "Queen", "Jack", "Ten", "Nine", "Eight"]
}
};
class DiceSimulator {
/**
* Creates a simulator for specialized dice configurations.
* @param {Object|null} customDice - Optional dice configuration override.
* @returns {DiceSimulator} - Simulator instance.
*/
constructor(customDice = null) {
this.results = [];
this.diceConfig = customDice || DICE_VARIATIONS;
}
/**
* Rolls one value from the provided face list.
* @param {Array<number|string>} faces - Available faces for the die.
* @returns {number|string} - Selected die face.
*/
rollDie(faces) {
return faces[Math.floor(Math.random() * faces.length)];
}
/**
* Calculates theoretical sum probabilities for the configured Sicherman dice.
* @returns {Array<{sum: number, probability: string}>} - Sum probability rows.
*/
calculateSichermanProbabilities() {
const probabilities = /* @__PURE__ */ new Map();
const die1 = this.diceConfig.sicherman.die1;
const die2 = this.diceConfig.sicherman.die2;
for (let v1 of die1) {
for (let v2 of die2) {
const sum = v1 + v2;
probabilities.set(sum, (probabilities.get(sum) || 0) + 1);
}
}
const totalOutcomes = die1.length * die2.length;
return Array.from(probabilities.entries()).map(([sum, count]) => ({
sum,
probability: (count / totalOutcomes * 100).toFixed(2) + "%"
})).sort((a, b) => a.sum - b.sum);
}
/**
* Calculates pairwise win probabilities for the intransitive dice set.
* @returns {Object<string, string>} - Win probabilities by dice matchup.
*/
calculateIntransitiveProbabilities() {
const dieA = this.diceConfig.intransitive.dieA;
const dieB = this.diceConfig.intransitive.dieB;
const dieC = this.diceConfig.intransitive.dieC;
const calculateWinProbability = (die1, die2) => {
let wins = 0;
let total = 0;
for (let v1 of die1) {
for (let v2 of die2) {
if (v1 > v2) wins++;
total++;
}
}
return (wins / total * 100).toFixed(2) + "%";
};
return {
"A vs B": calculateWinProbability(dieA, dieB),
"B vs C": calculateWinProbability(dieB, dieC),
"C vs A": calculateWinProbability(dieC, dieA)
};
}
/**
* Classifies a poker dice hand.
* @param {Array<number|string>} hand - Rolled poker dice faces.
* @returns {string} - Poker hand label.
*/
analyzePokerHand(hand) {
const valueMap = { "A": 14, "K": 13, "Q": 12, "J": 11 };
const numericHand = hand.map(
(card) => valueMap[card] || parseInt(card)
).sort((a, b) => b - a);
const frequencies = /* @__PURE__ */ new Map();
numericHand.forEach(
(value) => frequencies.set(value, (frequencies.get(value) || 0) + 1)
);
const counts = Array.from(frequencies.values()).sort((a, b) => b - a);
if (counts[0] === 5) return "Five of a kind";
if (counts[0] === 4) return "Four of a kind";
if (counts[0] === 3 && counts[1] === 2) return "Full house";
if (counts[0] === 3) return "Three of a kind";
if (counts[0] === 2 && counts[1] === 2) return "Two pair";
if (counts[0] === 2) return "One pair";
let isStrait = true;
for (let i = 1; i < numericHand.length; i++) {
if (numericHand[i] !== numericHand[i - 1] - 1) {
isStrait = false;
break;
}
}
if (isStrait) return "Straight";
return "High card";
}
/**
* Simulates Sicherman dice rolls.
* @param {number} rolls - Number of two-dice rolls to simulate.
* @returns {{rolls: string, probabilities: Array<{sum: number, probability: string}>}} - Formatted rolls and probability rows.
*/
simulateSicherman(rolls = 1) {
this.results = [];
const probabilities = this.calculateSichermanProbabilities();
for (let i = 0; i < rolls; i++) {
const die1 = this.rollDie(this.diceConfig.sicherman.die1);
const die2 = this.rollDie(this.diceConfig.sicherman.die2);
this.results.push({
die1,
die2,
sum: die1 + die2
});
}
return {
rolls: this.formatSichermanResults(),
probabilities
};
}
/**
* Simulates intransitive dice rolls.
* @param {number} rolls - Number of three-dice rolls to simulate.
* @returns {{rolls: string, probabilities: Object<string, string>}} - Formatted rolls and matchup probabilities.
*/
simulateIntransitive(rolls = 1) {
this.results = [];
const probabilities = this.calculateIntransitiveProbabilities();
for (let i = 0; i < rolls; i++) {
const dieA = this.rollDie(this.diceConfig.intransitive.dieA);
const dieB = this.rollDie(this.diceConfig.intransitive.dieB);
const dieC = this.rollDie(this.diceConfig.intransitive.dieC);
this.results.push({ dieA, dieB, dieC });
}
return {
rolls: this.formatIntransitiveResults(),
probabilities
};
}
/**
* Simulates a poker dice hand.
* @param {number} rolls - Number of dice to roll.
* @param {string} diceVariation - Poker dice variation key.
* @returns {{hand: string, analysis: string, probabilities: Object<string, string>}} - Formatted hand, analysis, and probabilities.
*/
simulatePoker(rolls = 5, diceVariation = "standard") {
this.results = [];
for (let i = 0; i < rolls; i++) {
this.results.push(this.rollDie(this.diceConfig.poker[diceVariation]));
}
const hand = this.results;
const handType = this.analyzePokerHand(hand);
return {
hand: this.formatPokerResults(),
analysis: handType,
probabilities: this.calculatePokerProbabilities(diceVariation)
};
}
/**
* Returns theoretical poker dice hand probabilities.
* @param {string} diceVariation - Poker dice variation key.
* @returns {Object<string, string>} - Probability labels by hand type.
*/
calculatePokerProbabilities(diceVariation) {
const probabilities = {
"Five of a kind": "0.08%",
"Four of a kind": "1.93%",
"Full house": "3.86%",
"Three of a kind": "15.43%",
"Two pair": "23.15%",
"One pair": "46.30%",
"Straight": "1.54%",
"High card": "7.71%"
};
return probabilities;
}
/**
* Formats the most recent Sicherman simulation.
* @returns {string} - Multiline Sicherman roll report.
*/
formatSichermanResults() {
return this.results.map(
(roll, index) => `Roll ${index + 1}: Die 1 = ${roll.die1}, Die 2 = ${roll.die2}, Sum = ${roll.sum}`
).join("\n");
}
/**
* Formats the most recent intransitive dice simulation.
* @returns {string} - Multiline intransitive roll report.
*/
formatIntransitiveResults() {
return this.results.map(
(roll, index) => `Roll ${index + 1}: Die A = ${roll.dieA}, Die B = ${roll.dieB}, Die C = ${roll.dieC}`
).join("\n");
}
/**
* Formats the most recent poker dice hand.
* @returns {string} - Poker hand report.
*/
formatPokerResults() {
return `Poker Dice Hand: ${this.results.join(" ")}`;
}
/**
* Adds a custom dice configuration to the simulator.
* @param {string} name - Custom dice configuration name.
* @param {Array<number|string>} faces - Faces available for the custom die.
* @returns {void}
*/
addCustomDiceConfiguration(name, faces) {
if (!this.diceConfig.custom) {
this.diceConfig.custom = {};
}
this.diceConfig.custom[name] = faces;
}
}
function simulateDice(diceType, numberOfRolls = 1, options = {}) {
const simulator = new DiceSimulator();
if (options.customDice) {
simulator.addCustomDiceConfiguration(options.customDice.name, options.customDice.faces);
}
switch (diceType.toLowerCase()) {
case "sicherman":
return simulator.simulateSicherman(numberOfRolls);
case "intransitive":
return simulator.simulateIntransitive(numberOfRolls);
case "poker":
return simulator.simulatePoker(5, options.pokerVariation || "standard");
default:
return 'Invalid dice type. Please choose "sicherman", "intransitive", or "poker"';
}
}
let finalResult = ``;
if (specializedDice === "sicherman") {
const sichermanResult = simulateDice("sicherman", numDice);
finalResult += `<mark>-- **Sicherman Dice. Dice#:** ${numDice} --</mark>
`;
finalResult += `**Rolls:**
${sichermanResult.rolls}
`;
if (addProb) {
if (Array.isArray(sichermanResult.probabilities)) {
finalResult += "**Probabilities:**\n";
sichermanResult.probabilities.forEach((item) => {
if (item.sum !== void 0 && item.probability !== void 0) {
finalResult += `Sum: ${item.sum}, Probability: ${item.probability}
`;
} else {
console.error("Invalid item structure:", item);
}
});
} else {
console.error("Probabilities is not an array:", sichermanResult.probabilities);
}
}
} else if (specializedDice === "intransitive") {
const intransitiveResult = simulateDice("intransitive", numDice);
finalResult += `<mark>-- **Intransitive Dice. Dice#:** ${numDice} --</mark>
`;
finalResult += `**Rolls:**
${intransitiveResult.rolls}
`;
if (addProb) {
finalResult += "**Probabilities:**\n";
finalResult += `"A vs B": "55.56%",
"B vs C": "55.56%",
"C vs A": "55.56%"`;
}
} else if (specializedDice === "poker") {
const pokerResult = simulateDice("poker", numDice, { pokerVariation: pokerVari });
finalResult += `<mark>-- **Poker Dice. Variation: ${pokerVari}. Dice#:** ${numDice}. --</mark>
`;
finalResult += `**Analysis:** ${pokerResult.analysis}
`;
finalResult += `**Hand:** ${pokerResult.hand}
`;
if (addProb) {
finalResult += "**Probabilities:**\n";
finalResult += `"Five of a kind": "0.08%",
"Four of a kind": "1.93%",
"Full house": "3.86%",
"Three of a kind": "15.43%",
"Two pair": "23.15%",
"One pair": "46.30%",
"Straight": "1.54%",
"High card": "7.71%"`;
}
}
const now = /* @__PURE__ */ new Date();
const YYMMDD = now.toISOString().slice(2, 10).replace(/-/g, "");
const HHMMSS = now.toTimeString().slice(0, 8).replace(/:/g, "");
const auditNoteName = `Dice Results Audit`;
const auditTagName = ["-reports/-dice"];
const auditnoteUUID = await getNoteUUID(app, auditNoteName, auditTagName);
const finalResultz = `[Report][^ADV]
[^ADV]: []()${finalResult}
`;
(async () => {
try {
const auditReport = `- <mark>Specialized:</mark> ***When:** ${YYMMDD}_${HHMMSS}*; <mark>**Dice rolled Report:**</mark> ${finalResultz}`;
await app.insertNoteContent({ uuid: auditnoteUUID }, auditReport);
await app.navigate(`https://www.amplenote.com/notes/${auditnoteUUID}`);
} catch (error) {
console.error(error.message);
}
})();
}
 
// anp-19-dice/lib/8_ball.js
async function ball_default(app) {
const result = await app.prompt("Consult the Magic 8-Ball", {
inputs: [
{ label: "Your Question", placeholder: 'Ask a direct yes/no question (e.g., "Will I achieve my goal today?")', type: "text" }
]
});
let answer;
if (result) {
let magic8Ball = function() {
const answers = [
"It is certain.",
"It is decidedly so.",
"Without a doubt.",
"Yes - definitely.",
"You may rely on it.",
"As I see it, yes.",
"Most likely.",
"Outlook good.",
"Yes.",
"Signs point to yes.",
"Reply hazy, try again.",
"Ask again later.",
"Better not tell you now.",
"Cannot predict now.",
"Concentrate and ask again.",
"Don't count on it.",
"My reply is no.",
"My sources say no.",
"Outlook not so good.",
"Very doubtful."
];
answer = answers[Math.floor(Math.random() * answers.length)];
};
magic8Ball();
const now = /* @__PURE__ */ new Date();
const YYMMDD = now.toISOString().slice(2, 10).replace(/-/g, "");
const HHMMSS = now.toTimeString().slice(0, 8).replace(/:/g, "");
const auditNoteName = `Dice Results Audit`;
const auditTagName = ["-reports/-dice"];
const auditnoteUUID = await getNoteUUID(app, auditNoteName, auditTagName);
(async () => {
try {
const auditReport = `- <mark>8 Ball:</mark> ***When:** ${YYMMDD}_${HHMMSS}*; **Question: ${result || "In Memory!"}**; <mark>**Answer:** ${answer}</mark>`;
await app.insertNoteContent({ uuid: auditnoteUUID }, auditReport);
await app.navigate(`https://www.amplenote.com/notes/${auditnoteUUID}`);
} catch (error) {
console.error(error.message);
}
})();
}
}
 
// anp-19-dice/lib/ask_sai_baba.js
var inMemoryCache = null;
async function loadSaiBabaAnswers(app) {
if (inMemoryCache && inMemoryCache.length > 0) {
return inMemoryCache;
}
const cacheKey = "Dice_Cache_Sai_Baba_Answers";
try {
if (typeof localStorage !== "undefined") {
const savedStr = localStorage.getItem(cacheKey);
if (savedStr) {
const parsed = JSON.parse(savedStr);
if (Array.isArray(parsed) && parsed.length > 0) {
inMemoryCache = parsed;
return inMemoryCache;
}
}
}
} catch (e) {
console.warn("Failed to read Sai Baba answers from localStorage:", e);
}
const remoteUrls = [
"https://raw.githubusercontent.com/krishnakanthb13/anp-19-dice/main/lib/data/sai-baba-answers.json",
"https://raw.githubusercontent.com/krishnakanthb13/anp-19-dice/refs/heads/main/lib/data/sai-baba-answers.json",
"https://cdn.jsdelivr.net/gh/krishnakanthb13/anp-19-dice@main/lib/data/sai-baba-answers.json"
];
for (const url of remoteUrls) {
try {
const res = await fetch(url);
if (res && res.ok) {
const answers = await res.json();
if (Array.isArray(answers) && answers.length > 0) {
inMemoryCache = answers;
try {
if (typeof localStorage !== "undefined") {
localStorage.setItem(cacheKey, JSON.stringify(answers));
}
} catch (saveErr) {
console.warn("Failed to persist Sai Baba answers to localStorage:", saveErr);
}
return inMemoryCache;
}
} else {
console.warn(`Fetch from ${url} returned status: ${res ? res.status : "unknown"}`);
}
} catch (e) {
console.warn(`Network error fetching from ${url}:`, e);
}
}
if (app && typeof app.alert === "function") {
app.alert("Unable to load Sai Baba answers.\n\nAn active internet connection is required on first use to download the guidance dataset. Once downloaded, it works 100% offline.");
}
return null;
}
async function ask_sai_baba_default(app) {
const result = await app.prompt("Seek Shirdi Sai Baba's Guidance. Reflect deeply on your question, then let a number between 1 and 720 intuitively enter your mind. Type that number below to receive his answer.", {
inputs: [
{ label: "Your Question", placeholder: "Reflect deeply on your question. You can type it here or keep it in mind.", type: "text" },
{ label: "Enter an Intuitive Number (1-720)", placeholder: "Type a number from 1 to 720 that comes to mind", type: "string" },
{ label: "Select Random Number", type: "checkbox", value: false }
]
});
if (result) {
let showAnswer = function(inputNumber) {
if (!inputNumber) {
return answers[Math.floor(Math.random() * answers.length)];
}
return answers[inputNumber - 1];
};
const [question2SaiBaba, number2SaiBaba, randomNumber] = result;
const answers = await loadSaiBabaAnswers(app);
if (!answers || answers.length === 0) {
return;
}
let saibabasAnswer;
if (!randomNumber) {
if (number2SaiBaba >= 1 && number2SaiBaba <= 720) {
saibabasAnswer = showAnswer(number2SaiBaba);
} else {
console.error("Invalid number2SaiBaba: Must be between 1 and 720");
app.alert("Invalid number2SaiBaba: Must be between 1 and 720");
return;
}
} else {
saibabasAnswer = showAnswer();
}
const now = /* @__PURE__ */ new Date();
const YYMMDD = now.toISOString().slice(2, 10).replace(/-/g, "");
const HHMMSS = now.toTimeString().slice(0, 8).replace(/:/g, "");
const auditNoteName = `Dice Results Audit`;
const auditTagName = ["-reports/-dice"];
const auditnoteUUID = await getNoteUUID(app, auditNoteName, auditTagName);
(async () => {
try {
const auditReport = `- <mark>Ask Sai Baba:</mark> ***When:** ${YYMMDD}_${HHMMSS}*; **Question: ${question2SaiBaba || "In Memory!"}**; <mark>**Answer:** ${saibabasAnswer}</mark>`;
await app.insertNoteContent({ uuid: auditnoteUUID }, auditReport);
await app.navigate(`https://www.amplenote.com/notes/${auditnoteUUID}`);
} catch (error) {
console.error(error.message);
}
})();
}
}
 
// anp-19-dice/lib/fudge_fate.js
async function fudge_fate_default(app) {
const existingSetting = await app.settings["Previous_Roll_FF"];
async function rollFudgeDice(numDice = 4) {
const outcomes = ["-", " ", "+"];
let results = [];
let total = 0;
for (let i = 0; i < numDice; i++) {
const roll = Math.floor(Math.random() * 6);
const face = outcomes[Math.floor(roll / 2)];
results.push(face);
total += face === "+" ? 1 : face === "-" ? -1 : 0;
}
return { results, total };
}
async function main() {
const numDicez = existingSetting;
const result = await app.prompt("Roll Fudge/Fate Dice", {
inputs: [
{ label: "Number of Dice", type: "string", value: numDicez || 4 }
]
});
if (result) {
const numDiceInput = result;
const numDice = parseInt(numDiceInput, 10) || 4;
await app.setSetting("Previous_Roll_FF", numDice);
if (numDice <= 0) {
console.error("Number of dice must be a positive integer!");
return;
}
const { results, total } = await rollFudgeDice(numDice);
const now = /* @__PURE__ */ new Date();
const YYMMDD = now.toISOString().slice(2, 10).replace(/-/g, "");
const HHMMSS = now.toTimeString().slice(0, 8).replace(/:/g, "");
const auditNoteName = `Dice Results Audit`;
const auditTagName = ["-reports/-dice"];
const auditnoteUUID = await getNoteUUID(app, auditNoteName, auditTagName);
(async () => {
try {
const auditReport = `- <mark>Fudge/Fate:</mark> ***When:** ${YYMMDD}_${HHMMSS}*; **Options: ${numDice}**; <mark>**Dice rolled:** [${results.join(", ")}]; **Total:** ${total};</mark>`;
await app.insertNoteContent({ uuid: auditnoteUUID }, auditReport);
await app.navigate(`https://www.amplenote.com/notes/${auditnoteUUID}`);
} catch (error) {
console.error(error.message);
}
})();
}
}
main();
}
 
// anp-19-dice/lib/fantasy_age_stunt_single_roll.js
async function fantasy_age_stunt_single_roll_default(app) {
function rollDie() {
return Math.floor(Math.random() * 6) + 1;
}
async function rollFantasyAGE() {
let dice = [rollDie(), rollDie(), rollDie()];
let total = dice.reduce((sum, die) => sum + die, 0);
let stuntDie = dice[0];
let hasStunt = new Set(dice).size < 3;
let stuntPoints = hasStunt ? stuntDie : 0;
const now = /* @__PURE__ */ new Date();
const YYMMDD = now.toISOString().slice(2, 10).replace(/-/g, "");
const HHMMSS = now.toTimeString().slice(0, 8).replace(/:/g, "");
const auditNoteName = `Dice Results Audit`;
const auditTagName = ["-reports/-dice"];
const auditnoteUUID = await getNoteUUID(app, auditNoteName, auditTagName);
if (hasStunt) {
(async () => {
try {
const auditReport = `- <mark>Fantasy AGE Stunt - Single:</mark> ***When:** ${YYMMDD}_${HHMMSS}*; <mark>**Dice rolled:** [${dice.join(", ")}]; **Total:** ${total}; AYE! You rolled doubles! **Stunt Points:** ${stuntPoints};</mark>`;
await app.insertNoteContent({ uuid: auditnoteUUID }, auditReport);
await app.navigate(`https://www.amplenote.com/notes/${auditnoteUUID}`);
} catch (error) {
console.error(error.message);
}
})();
const messageResult = `Fantasy AGE Stunt Dice Result:
Dice rolled: [${dice.join(", ")}].
Total: ${total}.
> AYE! You rolled doubles! Stunt Points: ${stuntPoints}.`;
app.alert(messageResult);
} else {
(async () => {
try {
const auditReport = `- <mark>Fantasy AGE Stunt - Single:</mark> ***When:** ${YYMMDD}_${HHMMSS}*; <mark>**Dice rolled:** [${dice.join(", ")}]; **Total:** ${total}; **Stunt Points:** No stunt this time. Better Luck Next Time!;</mark>`;
await app.insertNoteContent({ uuid: auditnoteUUID }, auditReport);
await app.navigate(`https://www.amplenote.com/notes/${auditnoteUUID}`);
} catch (error) {
console.error(error.message);
}
})();
const messageResult = `Fantasy AGE Stunt Dice Result:
Dice rolled: ${dice.join(", ")}.
Total: ${total}.
> No stunt this time. Better Luck Next Time!`;
app.alert(messageResult);
}
}
rollFantasyAGE();
}
 
// anp-19-dice/lib/fantasy_age_stunt_roll_all_at_once.js
async function fantasy_age_stunt_roll_all_at_once_default(app) {
const existingSetting = await app.settings["Previous_Roll_AGE"];
let result;
if (existingSetting) {
const [
playerCountz,
charactersPerPlayerz
] = (existingSetting || "").split(",").map((value, index) => {
const defaults = [3, 2];
if (value === void 0 || value === null || value.trim() === "") {
return defaults[index];
}
if ([0, 1].includes(index)) return Number(value) || defaults[index];
return value;
});
result = await app.prompt("Roll Fantasy AGE Stunts (All Players)", {
inputs: [
{ label: "Number of Players", type: "string", value: playerCountz },
{ label: "Characters Per Player", type: "string", value: charactersPerPlayerz }
]
});
} else {
result = await app.prompt("Roll Fantasy AGE Stunts (All Players)", {
inputs: [
{ label: "Number of Players", type: "string", value: 3 },
{ label: "Characters Per Player", type: "string", value: 2 }
]
});
}
let finalResult = `**Fantasy AGE Stunt - Roll All At Once**`;
function rollDie() {
return Math.floor(Math.random() * 6) + 1;
}
function rollFantasyAGE(playerName, characterName) {
let dice = [rollDie(), rollDie(), rollDie()];
let total = dice.reduce((sum, die) => sum + die, 0);
let stuntDie = dice[0];
let hasStunt = new Set(dice).size < 3;
let stuntPoints = hasStunt ? stuntDie : 0;
finalResult += `<mark>
-- ${playerName}'s Character: ${characterName} --</mark>`;
finalResult += `
Dice rolled: ${dice.join(", ")}`;
finalResult += `
Total: ${total}`;
if (hasStunt) {
finalResult += `
AYE! You rolled doubles! Stunt Points: ${stuntPoints}`;
} else {
finalResult += `
No stunt this time. Better Luck Next Time!`;
}
}
function playFantasyAGE(playerCount, charactersPerPlayer) {
for (let i = 1; i <= playerCount; i++) {
let playerName = `Player ${i}`;
for (let j = 1; j <= charactersPerPlayer; j++) {
let characterName = `Character ${j}`;
rollFantasyAGE(playerName, characterName);
}
}
}
if (result) {
const [
playerCount,
// Total number of players
charactersPerPlayer
// Number of characters each player controls
] = result;
await app.setSetting("Previous_Roll_AGE", result);
playFantasyAGE(playerCount, charactersPerPlayer);
const now = /* @__PURE__ */ new Date();
const YYMMDD = now.toISOString().slice(2, 10).replace(/-/g, "");
const HHMMSS = now.toTimeString().slice(0, 8).replace(/:/g, "");
const auditNoteName = `Dice Results Audit`;
const auditTagName = ["-reports/-dice"];
const auditnoteUUID = await getNoteUUID(app, auditNoteName, auditTagName);
const finalResultz = `[Report][^AGER]
[^AGER]: []()${finalResult}
`;
const auditReport = `- <mark>Fantasy AGE Stunt - All:</mark> ***When:** ${YYMMDD}_${HHMMSS}*; <mark>**Player Count:** ${playerCount}; **Characters Per Player:** ${charactersPerPlayer}; **Stunt Points:**</mark> ${finalResultz}`;
await app.insertNoteContent({ uuid: auditnoteUUID }, auditReport);
await app.navigate(`https://www.amplenote.com/notes/${auditnoteUUID}`);
app.alert(finalResult);
}
}
 
// anp-19-dice/lib/table_randomizer.js
async function table_randomizer_default(app, noteUUID) {
let markdown;
try {
markdown = await app.getNoteContent({ uuid: noteUUID });
if (!markdown) throw new Error("No content found");
} catch (err) {
console.error("Error retrieving note content:", err);
app.alert("Failed to read note content. Please ensure a valid note is selected.");
return;
}
const removeHtmlComments = (content) => content.replace(/<!--[\s\S]*?-->/g, "").trim();
const removeEmptyRowsAndColumns = (table) => {
const rows = table.split("\n").filter((row) => row.trim().startsWith("|"));
const filteredRows = rows.filter((row) => {
const cells = row.split("|").slice(1, -1);
const hasContent = cells.some((cell) => cell.trim() !== "");
return hasContent;
});
if (filteredRows.length === 0) {
return "";
}
const columnCount = filteredRows[0].split("|").length - 2;
const nonEmptyColumns = Array.from(
{ length: columnCount },
(_, colIndex) => filteredRows.some((row) => row.split("|")[colIndex + 1].trim() !== "")
);
const cleanedRows = filteredRows.map((row) => {
const cells = row.split("|").slice(1, -1);
const filteredCells = cells.filter((_, i) => nonEmptyColumns[i]);
return `| ${filteredCells.join(" | ")} |`;
});
return cleanedRows.join("\n");
};
const lines = markdown.split("\n");
let tableCount = 0;
let inTable = false;
const tables = [];
let currentTable = [];
lines.forEach((line, index) => {
if (line.trim().startsWith("|")) {
if (!inTable) {
tableCount++;
if (tableCount > 1) {
tables.push("---");
}
tables.push(`# Table ${tableCount}
`);
inTable = true;
}
if (currentTable.length === 0 && line.split("|").every((cell) => cell.trim() === "")) {
const columnCount = line.split("|").length - 2;
const headers = Array.from({ length: columnCount }, (_, i) => `Column ${i + 1}`).join(" | ");
}
currentTable.push(line);
} else if (inTable) {
inTable = false;
const tableContent = currentTable.join("\n");
tables.push(tableContent);
tables.push("");
currentTable = [];
}
});
if (currentTable.length > 0) {
const tableContent = currentTable.join("\n");
tables.push(removeEmptyRowsAndColumns(tableContent));
}
const processedContent = tables.join("\n\n");
const cleanedContent = removeHtmlComments(processedContent);
const markdownText = cleanedContent;
function getTableDetails(markdownContent) {
const headerRegex = /#\s*Table\s*\d+/g;
const headers = markdownContent.match(headerRegex);
if (!headers) return [];
const tableDetails = headers.map((header) => {
const tableName = header.replace("# ", "");
return { label: tableName, value: tableName };
});
tableDetails.unshift({ label: "All Tables", value: "All" });
return tableDetails;
}
const numberOfTables = getTableDetails(markdownText);
if (numberOfTables < 1) {
app.alert("Warning: This Note does not contain any Tables. Select this option on Notes which contain Tables.");
return;
}
const existingSetting = await app.settings["Previous_Roll_Ran"];
const result = await app.prompt(
"Configure Table Randomizer",
{
inputs: [
{
label: "Choose Table to Randomize",
type: "radio",
options: numberOfTables,
value: "All"
},
{ label: "Select number of Randomizations.", type: "string", value: existingSetting || 3 },
{ label: "First row is header (skip from selection)", type: "checkbox", value: true }
]
}
);
if (result) {
const [
nthTable,
// User-defined parameter to select the nth table
numberCombo,
// Number of combinations
hasHeader
// Specify whether the table has headers to skip the first row of data
] = result;
await app.setSetting("Previous_Roll_Ran", numberCombo);
class ColumnRandomPicker {
/**
* Creates a random picker for markdown tables.
* @param {string} markdownText - Processed markdown containing named tables.
* @param {boolean} keepHeaders - Whether source tables include headers.
* @returns {ColumnRandomPicker} - Picker instance with parsed tables.
*/
constructor(markdownText2, keepHeaders = true) {
this.keepHeaders = keepHeaders;
this.tables = this.parseTables(markdownText2);
}
/**
* Parses numbered markdown tables into a table map.
* @param {string} markdownText - Markdown containing generated table headings.
* @returns {Object<string, string[][]>} - Table data keyed by table name.
*/
parseTables(markdownText2) {
const tables2 = {};
let currentTable2 = [];
let currentTableName = "";
const lines2 = markdownText2.split("\n");
for (const line of lines2) {
if (line.startsWith("# ")) {
if (currentTable2.length > 0) {
tables2[currentTableName] = this.processTable(currentTable2);
currentTable2 = [];
}
currentTableName = line.substring(2).trim();
} else if (line.includes("|") && !line.trim().startsWith("---")) {
currentTable2.push(line);
}
}
if (currentTable2.length > 0 && currentTableName) {
tables2[currentTableName] = this.processTable(currentTable2);
}
return tables2;
}
/**
* Converts table lines into cell arrays, skipping markdown separator/header rows.
* @param {string[]} tableLines - Raw markdown table lines.
* @returns {string[][]} - Parsed table body cells.
*/
processTable(tableLines) {
const startIndex = this.keepHeaders ? 3 : 2;
const data = tableLines.slice(startIndex).map((line) => {
return line.split("|").slice(1, -1).map((cell) => cell.trim());
});
return data;
}
/**
* Picks one non-empty value from an array.
* @param {string[]} arr - Candidate values.
* @returns {string} - Random value, or an empty string when none are available.
*/
getRandomValueFromArray(arr) {
const validValues = arr.filter((value) => value !== "");
if (validValues.length === 0) return "";
return validValues[Math.floor(Math.random() * validValues.length)];
}
/**
* Picks one random value from each column of a table.
* @param {string} tableName - Name of the parsed table.
* @returns {string[]|null} - Column-based random combination, or null if missing.
*/
getColumnBasedRandomCombination(tableName) {
const table = this.tables[tableName];
if (!table) return null;
const numColumns = table[0].length;
const columns = Array(numColumns).fill().map(() => []);
table.forEach((row) => {
row.forEach((value, colIndex) => {
if (value !== "") {
columns[colIndex].push(value);
}
});
});
return columns.map((column) => this.getRandomValueFromArray(column));
}
/**
* Generates multiple random combinations for one table.
* @param {string} tableName - Name of the parsed table.
* @param {number} count - Number of combinations to generate.
* @returns {string[][]} - Generated combinations.
*/
generateMultipleCombinations(tableName, count) {
const combinations = [];
for (let i = 0; i < count; i++) {
const combo = this.getColumnBasedRandomCombination(tableName);
if (combo) combinations.push(combo);
}
return combinations;
}
/**
* Generates random combinations for every parsed table.
* @param {number} count - Number of combinations per table.
* @returns {Object<string, string[][]>} - Generated combinations keyed by table name.
*/
generateCombinationsForAllTables(count = 1) {
const result2 = {};
for (const tableName of Object.keys(this.tables)) {
result2[tableName] = this.generateMultipleCombinations(tableName, count);
}
return result2;
}
/**
* Generates random combinations for a single parsed table.
* @param {string} tableName - Name of the parsed table.
* @param {number} count - Number of combinations to generate.
* @returns {Object<string, string[][]>} - Generated combinations keyed by table name.
*/
generateCombinationsForOneTable(tableName, count = 1) {
const result2 = {};
if (this.tables[tableName]) {
result2[tableName] = this.generateMultipleCombinations(tableName, count);
} else {
console.error("Table not found: ", tableName);
}
return result2;
}
/**
* Formats generated combinations as markdown tables.
* @param {Object<string, string[][]>|string[][]} combinations - Generated random combinations.
* @returns {string} - Markdown report.
*/
formatAsMarkdown(combinations) {
let output = "";
if (Array.isArray(combinations)) {
const tableData = Object.entries(combinations);
output += `<mark>Table</mark>
`;
output += "|" + tableData[0].map((_, i) => ` Column ${i + 1} `).join("|") + "|\n";
output += "|" + tableData[0].map(() => "---").join("|") + "|\n";
tableData.forEach((row) => {
output += "|" + row.map((cell) => ` ${cell} `).join("|") + "|\n";
});
} else {
for (const [tableName, tableData] of Object.entries(combinations)) {
output += `<mark>${tableName}</mark>
`;
output += "|" + tableData[0].map((_, i) => ` Column ${i + 1} `).join("|") + "|\n";
output += "|" + tableData[0].map(() => "---").join("|") + "|\n";
tableData.forEach((row) => {
output += "|" + row.map((cell) => ` ${cell} `).join("|") + "|\n";
});
output += "\n---\n";
}
}
return output;
}
}
const picker = new ColumnRandomPicker(markdownText, hasHeader);
let finalOutput;
if (nthTable === "All") {
const multipleCombinations = picker.generateCombinationsForAllTables(numberCombo);
finalOutput = picker.formatAsMarkdown(multipleCombinations);
} else {
const tableCombo = picker.generateCombinationsForOneTable(nthTable, numberCombo);
finalOutput = picker.formatAsMarkdown(tableCombo);
}
const now = /* @__PURE__ */ new Date();
const YYMMDD = now.toISOString().slice(2, 10).replace(/-/g, "");
const HHMMSS = now.toTimeString().slice(0, 8).replace(/:/g, "");
const auditNoteName = `Dice Results Audit`;
const auditTagName = ["-reports/-dice"];
const auditnoteUUID = await getNoteUUID(app, auditNoteName, auditTagName);
const finalResultz = `[Report][^AGER]
[^AGER]: []()${finalOutput}
`;
const auditReport = `- <mark>Table - Randomizer:</mark> ***When:** ${YYMMDD}_${HHMMSS}*; **UUID:** ${noteUUID} ; <mark>**Data:**</mark> ${finalResultz}`;
await app.insertNoteContent({ uuid: auditnoteUUID }, auditReport);
const safeUUID = String(auditnoteUUID);
await app.navigate(`https://www.amplenote.com/notes/${safeUUID}`);
app.alert(finalOutput);
}
}
 
// anp-19-dice/lib/history.js
async function clearAuditHistory(app) {
const confirm = await app.prompt("Confirm Deletion", {
inputs: [{ label: "Type 'YES' to clear all dice audit history", type: "string" }]
});
if (confirm && confirm.trim().toUpperCase() === "YES") {
const uuid = await app.settings["Dice_Audit_UUID [Do not Edit!]"];
if (uuid) {
await app.replaceNoteContent({ uuid }, "");
app.alert("Audit history cleared!");
} else {
app.alert("No audit note found to clear.");
}
}
}
async function viewRollHistory(app) {
const uuid = await app.settings["Dice_Audit_UUID [Do not Edit!]"];
if (uuid) {
try {
const content = await app.getNoteContent({ uuid });
if (!content || content.trim() === "") {
app.alert("Audit history is empty. Roll some dice first!");
return;
}
await app.navigate(`https://www.amplenote.com/notes/${uuid}`);
} catch (error) {
app.alert("Could not access audit note.");
}
} else {
app.alert("No audit note found. Roll some dice first!");
}
}
 
// anp-19-dice/lib/quick_presets.js
async function quick_presets_default(app) {
const presets = {
"D&D Ability Score": { numDice: 4, faces: 6, keepHighest: true, keepCount: 3 },
"D20 Check": { numDice: 1, faces: 20 },
"D&D Fireball (8d6)": { numDice: 8, faces: 6 },
"D&D Attack (1d8+3)": { numDice: 1, faces: 8, min: 4, max: 11 },
"Savage Worlds": { numDice: 2, faces: 6, explode: true, explodeTarget: 6 },
"2d6 (standard)": { numDice: 2, faces: 6 },
"3d6 (GURPS)": { numDice: 3, faces: 6 },
"Fudge/Fate (4dF)": { numDice: 4, faces: "fudge", type: "fudge" },
"Coin Flip": { numDice: 1, faces: 2 },
"D66 Table": { numDice: 2, faces: 6, sort: "asc", unique: false },
"Custom Faces": { numDice: 2, faces: 6 }
// Included from step 5 of ds.md
};
const presetOptions = Object.keys(presets).map((key) => ({
label: key,
value: key
}));
const result = await app.prompt("Quick Roll Presets", {
inputs: [
{
label: "Select Preset",
type: "select",
options: presetOptions,
value: "D20 Check"
},
{
label: "Add Modifier (+/-)",
type: "string",
placeholder: "e.g., +3 or -2"
}
]
});
if (result) {
const [presetName, modifierStr] = result;
const preset = presets[presetName];
const modifier = parseInt(modifierStr) || 0;
let rolls, total;
if (preset.type === "fudge") {
const outcomes = ["-", " ", "+"];
rolls = [];
total = 0;
for (let i = 0; i < preset.numDice; i++) {
const roll = Math.floor(Math.random() * 6);
const face = outcomes[Math.floor(roll / 2)];
rolls.push(face);
total += face === "+" ? 1 : face === "-" ? -1 : 0;
}
} else {
const numDice = preset.numDice || 1;
const faces = preset.faces || 6;
const min = preset.min;
const max = preset.max;
rolls = [];
for (let i = 0; i < numDice; i++) {
let roll = Math.floor(Math.random() * faces) + 1;
if (min) roll = Math.max(roll, min);
if (max) roll = Math.min(roll, max);
rolls.push(roll);
}
if (preset.keepHighest) {
rolls.sort((a, b) => b - a);
rolls = rolls.slice(0, preset.keepCount);
}
if (preset.explode) {
const finalRolls = [];
for (let i = 0; i < rolls.length; i++) {
let currentRoll = rolls[i];
finalRolls.push(currentRoll);
while (currentRoll === preset.explodeTarget) {
currentRoll = Math.floor(Math.random() * faces) + 1;
finalRolls.push(currentRoll);
}
}
rolls = finalRolls;
}
if (preset.sort === "asc") rolls.sort((a, b) => a - b);
total = rolls.reduce((sum, r) => sum + r, 0);
}
const finalTotal = total + modifier;
let finalResult = `<mark>**Quick Roll: ${presetName}**</mark>
`;
finalResult += `**Dice:** [${rolls.join(", ")}]
`;
finalResult += `**Base Total:** ${total}
`;
if (modifier !== 0) finalResult += `**Modifier:** ${modifier > 0 ? "+" : ""}${modifier}
`;
finalResult += `**Final Total:** ${finalTotal}
`;
if (presetName === "Coin Flip") {
finalResult += `**Result:** ${rolls[0] === 1 ? "Heads" : "Tails"}
`;
}
const now = /* @__PURE__ */ new Date();
const YYMMDD = now.toISOString().slice(2, 10).replace(/-/g, "");
const HHMMSS = now.toTimeString().slice(0, 8).replace(/:/g, "");
const auditNoteName = `Dice Results Audit`;
const auditTagName = ["-reports/-dice"];
const auditnoteUUID = await getNoteUUID(app, auditNoteName, auditTagName);
(async () => {
try {
const auditReport = `- <mark>Quick Roll (${presetName}):</mark> ***When:** ${YYMMDD}_${HHMMSS}*; <mark>**Rolls:** [${rolls.join(", ")}], **Total:** ${finalTotal}</mark>`;
await app.insertNoteContent({ uuid: auditnoteUUID }, auditReport);
await app.navigate(`https://www.amplenote.com/notes/${auditnoteUUID}`);
app.alert(finalResult);
} catch (error) {
console.error(error.message);
}
})();
}
}
 
// anp-19-dice/lib/weighted_random.js
async function weighted_random_default(app) {
const existingSetting = await app.settings["Previous_Weighted"];
let result;
if (existingSetting) {
const [items, weights] = existingSetting.split("||");
result = await app.prompt("Weighted Random Selector", {
inputs: [
{
label: "Items (comma-separated)",
type: "text",
value: items,
placeholder: "e.g., Sword, Shield, Potion, Gold, Magic Ring"
},
{
label: "Weights (comma-separated, corresponding to items)",
type: "text",
value: weights,
placeholder: "e.g., 10, 20, 30, 25, 15"
}
]
});
} else {
result = await app.prompt("Weighted Random Selector", {
inputs: [
{
label: "Items (comma-separated)",
type: "text",
placeholder: "e.g., Sword, Shield, Potion, Gold, Magic Ring"
},
{
label: "Weights (comma-separated, corresponding to items)",
type: "text",
placeholder: "e.g., 10, 20, 30, 25, 15"
}
]
});
}
if (result) {
let weightedRandom = function(items2, weights2) {
const totalWeight2 = weights2.reduce((sum, w) => sum + w, 0);
let random = Math.random() * totalWeight2;
for (let i = 0; i < items2.length; i++) {
random -= weights2[i];
if (random <= 0) {
return items2[i];
}
}
return items2[items2.length - 1];
};
const [itemsStr, weightsStr] = result;
const items = itemsStr.split(",").map((i) => i.trim()).filter((i) => i);
const weights = weightsStr.split(",").map((w) => parseFloat(w.trim())).filter((w) => !isNaN(w) && w > 0);
if (items.length === 0 || weights.length === 0) {
app.alert("Please provide both items and weights.");
return;
}
if (items.length !== weights.length) {
app.alert("Number of items must match number of weights.");
return;
}
await app.setSetting("Previous_Weighted", `${itemsStr}||${weightsStr}`);
const selected = weightedRandom(items, weights);
const totalWeight = weights.reduce((sum, w) => sum + w, 0);
const probabilities = items.map((item, i) => ({
item,
weight: weights[i],
probability: (weights[i] / totalWeight * 100).toFixed(1)
}));
let finalResult = `<mark>**Weighted Random Selection**</mark>
`;
finalResult += `**Selected:** ${selected}
`;
finalResult += `**Probabilities:**
`;
probabilities.forEach((p) => {
finalResult += `.- ${p.item}: ${p.probability}% (weight: ${p.weight})
`;
});
const now = /* @__PURE__ */ new Date();
const YYMMDD = now.toISOString().slice(2, 10).replace(/-/g, "");
const HHMMSS = now.toTimeString().slice(0, 8).replace(/:/g, "");
const auditNoteName = `Dice Results Audit`;
const auditTagName = ["-reports/-dice"];
const auditnoteUUID = await getNoteUUID(app, auditNoteName, auditTagName);
(async () => {
try {
const auditReport = `- <mark>Weighted Random:</mark> ***When:** ${YYMMDD}_${HHMMSS}*; <mark>**Selected:** ${selected}</mark> | Items: ${items.join(", ")} | Weights: ${weights.join(", ")}`;
await app.insertNoteContent({ uuid: auditnoteUUID }, auditReport);
await app.navigate(`https://www.amplenote.com/notes/${auditnoteUUID}`);
app.alert(finalResult);
} catch (error) {
console.error(error.message);
}
})();
}
}
 
// anp-19-dice/lib/dice_pool.js
async function dice_pool_default(app) {
const existingSetting = await app.settings["Previous_DicePool"];
let parsedDefaults = { poolSize: 5, targetNumber: 5, explode: false, countType: "hits" };
if (existingSetting) {
try {
const parts = existingSetting.split(",");
parsedDefaults.poolSize = parseInt(parts[0]) || 5;
parsedDefaults.targetNumber = parseInt(parts[1]) || 5;
parsedDefaults.explode = parts[2] === "true";
parsedDefaults.countType = parts[3] || "hits";
} catch (e) {
}
}
const result = await app.prompt("Dice Pool Roller", {
inputs: [
{
label: "Number of Dice in Pool",
type: "string",
value: parsedDefaults.poolSize.toString()
},
{
label: "Target Number (TN) for Success",
type: "string",
value: parsedDefaults.targetNumber.toString()
},
{
label: "Exploding 6s (re-roll on 6)",
type: "checkbox",
value: parsedDefaults.explode
},
{
label: "Count Method",
type: "select",
options: [
{ label: "Count Hits (dice >= TN)", value: "hits" },
{ label: "Sum All Dice", value: "sum" },
{ label: "Count Successes (TN as difficulty)", value: "successes" }
],
value: parsedDefaults.countType
}
]
});
if (result) {
let rollPool = function(size, tn, exploding = false) {
let rolls = [];
let successes = 0;
let ones = 0;
for (let i = 0; i < size; i++) {
let roll = Math.floor(Math.random() * 6) + 1;
rolls.push(roll);
if (roll === 1) ones++;
if (roll >= tn) successes++;
while (exploding && roll === 6) {
roll = Math.floor(Math.random() * 6) + 1;
rolls.push(roll);
if (roll >= tn) successes++;
if (roll === 1) ones++;
}
}
return { rolls, successes, ones };
};
const [poolSizeStr, targetNumberStr, explode, countType] = result;
const poolSize = parseInt(poolSizeStr) || 5;
const targetNumber = parseInt(targetNumberStr) || 5;
await app.setSetting("Previous_DicePool", `${poolSize},${targetNumber},${explode},${countType}`);
const result2 = rollPool(poolSize, targetNumber, explode);
let finalResult = `<mark>**Dice Pool Results**</mark>
`;
finalResult += `**Pool Size:** ${poolSize}d6
`;
finalResult += `**Target Number:** ${targetNumber}+
`;
finalResult += `**Dice Rolled:** [${result2.rolls.join(", ")}]
`;
finalResult += `**Successes (>=${targetNumber}):** ${result2.successes}
`;
finalResult += `**Ones (1s):** ${result2.ones}
`;
finalResult += `**Net Successes:** ${result2.successes - result2.ones}
`;
finalResult += `**Roll Details:**
`;
for (let i = 0; i < Math.min(result2.rolls.length, poolSize); i++) {
const roll = result2.rolls[i];
let indicator = roll >= targetNumber ? "\u2713" : roll === 1 ? "\u2717" : "-";
finalResult += `Die ${i + 1}: ${roll} ${indicator}
`;
}
const now = /* @__PURE__ */ new Date();
const YYMMDD = now.toISOString().slice(2, 10).replace(/-/g, "");
const HHMMSS = now.toTimeString().slice(0, 8).replace(/:/g, "");
const auditNoteName = `Dice Results Audit`;
const auditTagName = ["-reports/-dice"];
const auditnoteUUID = await getNoteUUID(app, auditNoteName, auditTagName);
(async () => {
try {
const auditReport = `- <mark>Dice Pool:</mark> ***When:** ${YYMMDD}_${HHMMSS}*; Pool: ${poolSize}d6, TN: ${targetNumber}, Explode: ${explode} | <mark>**Successes:** ${result2.successes}, **Net:** ${result2.successes - result2.ones}</mark> | Rolls: [${result2.rolls.join(", ")}]`;
await app.insertNoteContent({ uuid: auditnoteUUID }, auditReport);
await app.navigate(`https://www.amplenote.com/notes/${auditnoteUUID}`);
app.alert(finalResult);
} catch (error) {
console.error(error.message);
}
})();
}
}
 
// anp-19-dice/lib/decision_matrix.js
async function decision_matrix_default(app) {
const result = await app.prompt("Decision Matrix Setup", {
inputs: [
{
label: "Options (comma-separated)",
type: "text",
placeholder: "e.g., Option A, Option B, Option C"
},
{
label: "Criteria (comma-separated)",
type: "text",
placeholder: "e.g., Cost, Time, Quality, Risk"
},
{
label: "Criteria Weights (comma-separated, 1-10)",
type: "text",
placeholder: "e.g., 8, 5, 9, 3"
}
]
});
if (result) {
const [optionsStr, criteriaStr, weightsStr] = result;
const options = optionsStr.split(",").map((o) => o.trim()).filter((o) => o);
const criteria = criteriaStr.split(",").map((c) => c.trim()).filter((c) => c);
const weights = weightsStr.split(",").map((w) => parseFloat(w.trim())).filter((w) => !isNaN(w));
if (options.length < 2 || criteria.length < 2) {
app.alert("Need at least 2 options and 2 criteria.");
return;
}
if (criteria.length !== weights.length) {
app.alert("Number of criteria must match number of weights.");
return;
}
const scores = [];
for (let i = 0; i < options.length; i++) {
scores[i] = [];
for (let j = 0; j < criteria.length; j++) {
scores[i][j] = Math.floor(Math.random() * 10) + 1;
}
}
const weightedScores = options.map((option, i) => {
let total = 0;
criteria.forEach((criterion, j) => {
total += scores[i][j] * weights[j];
});
return { option, total, scores: scores[i] };
});
weightedScores.sort((a, b) => b.total - a.total);
let finalResult = `<mark>**Decision Matrix Results**</mark>
`;
finalResult += `| Option | ${criteria.join(" | ")} | **Total** |
`;
finalResult += `|${"---|".repeat(criteria.length + 2)}
`;
weightedScores.forEach((item) => {
finalResult += `| ${item.option} | ${item.scores.join(" | ")} | **${item.total}** |
`;
});
finalResult += `<mark>**Rankings:**</mark>
`;
weightedScores.forEach((item, i) => {
const medal = i === 0 ? "\u{1F947}" : i === 1 ? "\u{1F948}" : i === 2 ? "\u{1F949}" : `.${i + 1}.`;
finalResult += `${medal} **${item.option}** - Score: ${item.total}
`;
});
finalResult += `**Criteria Weights:** ${criteria.map((c, i) => `${c}: ${weights[i]}`).join(", ")}`;
const now = /* @__PURE__ */ new Date();
const YYMMDD = now.toISOString().slice(2, 10).replace(/-/g, "");
const HHMMSS = now.toTimeString().slice(0, 8).replace(/:/g, "");
const auditNoteName = `Dice Results Audit`;
const auditTagName = ["-reports/-dice"];
const auditnoteUUID = await getNoteUUID(app, auditNoteName, auditTagName);
(async () => {
try {
const winner = weightedScores[0];
const breakdown = weightedScores.map((ws) => `${ws.option}: ${ws.total}`).join(", ");
const auditReport = `- <mark>Decision Matrix:</mark> ***When:** ${YYMMDD}_${HHMMSS}*; <mark>**Winner:** ${winner.option} (Score: ${winner.total})</mark> | Breakdown: [${breakdown}] | Criteria: ${criteria.join(", ")}`;
await app.insertNoteContent({ uuid: auditnoteUUID }, auditReport);
await app.navigate(`https://www.amplenote.com/notes/${auditnoteUUID}`);
app.alert(finalResult);
} catch (error) {
console.error(error.message);
}
})();
}
}
 
// anp-19-dice/lib/name_generator.js
async function name_generator_default(app) {
const nameStyles = {
fantasy: {
prefixes: ["Al", "An", "Ar", "Bal", "Bel", "Bor", "Bri", "Cor", "Dar", "El", "Eld", "Far", "Gal", "Gil", "Hal", "Is", "Kal", "Kil", "Lan", "Mor", "Nor", "Pal", "Quin", "Ral", "Sam", "Tal", "Thal", "Ul", "Val", "Wil", "Xan", "Yor", "Zan"],
suffixes: ["dor", "mir", "ion", "gar", "mar", "nar", "rin", "thir", "wen", "wyn", "lor", "din", "dan", "bar", "nor", "lan", "dar", "ran", "reth", "las", "mas", "nir", "ril", "dis", "ric", "mond", "ton", "ley", "burg", "heim"]
},
scifi: {
prefixes: ["Ax", "Cy", "Dex", "Echo", "Flux", "Geo", "Hex", "Ion", "Jax", "Kai", "Lex", "Max", "Nex", "Onyx", "Pax", "Quark", "Rex", "Sol", "Tech", "Ultra", "Vex", "Warp", "Xen", "Yotta", "Zero"],
suffixes: ["oid", "ite", "ian", "ium", "ax", "ex", "ox", "ux", "on", "ar", "or", "us", "is", "os", "eon", "tron", "wave", "pulse", "beam", "core"]
},
norse: {
prefixes: ["As", "Bjorn", "Egil", "Fen", "Gunn", "Hal", "Ing", "Jar", "Knut", "Leif", "Magn", "Njord", "Odd", "Ragn", "Sig", "Thor", "Ulf", "Val", "Yng", "Odin"],
suffixes: ["ar", "ir", "ur", "olf", "bjorn", "stein", "vald", "mund", "mar", "rik", "ulf", "vard", "brand", "fast", "grim", "hild", "laug", "leif", "mod", "run"]
}
};
const result = await app.prompt("Random Name Generator", {
inputs: [
{
label: "Name Style",
type: "select",
options: [
{ label: "Fantasy", value: "fantasy" },
{ label: "Sci-Fi", value: "scifi" },
{ label: "Norse", value: "norse" },
{ label: "Mixed (Random Style)", value: "mixed" }
],
value: "fantasy"
},
{
label: "Number of Names to Generate",
type: "string",
value: "5"
},
{
label: "Include Titles/Prefixes",
type: "checkbox",
value: false
}
]
});
if (result) {
let generateName = function(styleName) {
let selectedStyle;
if (styleName === "mixed") {
const styles = Object.keys(nameStyles);
selectedStyle = nameStyles[styles[Math.floor(Math.random() * styles.length)]];
} else {
selectedStyle = nameStyles[styleName];
}
const prefix = selectedStyle.prefixes[Math.floor(Math.random() * selectedStyle.prefixes.length)];
const suffix = selectedStyle.suffixes[Math.floor(Math.random() * selectedStyle.suffixes.length)];
const name = prefix + suffix;
if (includeTitles) {
const title = titles[Math.floor(Math.random() * titles.length)];
return `${title} ${name}`;
}
return name;
};
const [style, countStr, includeTitles] = result;
const count = Math.min(parseInt(countStr) || 5, 20);
const titles = ["Sir", "Lady", "Lord", "Captain", "Commander", "Archmage", "King", "Queen", "Prince", "Princess", "Master", "Doctor", "Professor", "Admiral", "Baron"];
let finalResult = `<mark>**Generated Names (${style})**</mark>
Names: `;
const names = [];
for (let i = 0; i < count; i++) {
const name = generateName(style);
names.push(name);
}
finalResult += names.join(", ");
const now = /* @__PURE__ */ new Date();
const YYMMDD = now.toISOString().slice(2, 10).replace(/-/g, "");
const HHMMSS = now.toTimeString().slice(0, 8).replace(/:/g, "");
const auditNoteName = `Dice Results Audit`;
const auditTagName = ["-reports/-dice"];
const auditnoteUUID = await getNoteUUID(app, auditNoteName, auditTagName);
(async () => {
try {
const auditReport = `- <mark>Name Generator:</mark> ***When:** ${YYMMDD}_${HHMMSS}*; Style: ${style}, Count: ${count} | <mark>**Names:** ${names.join(", ")}</mark>`;
await app.insertNoteContent({ uuid: auditnoteUUID }, auditReport);
await app.navigate(`https://www.amplenote.com/notes/${auditnoteUUID}`);
app.alert(finalResult);
} catch (error) {
console.error(error.message);
}
})();
}
}
 
// anp-19-dice/lib/tarot.js
async function tarot_default(app) {
const majorArcana = [
{ name: "The Fool", number: 0, meaning: "New beginnings, innocence, spontaneity" },
{ name: "The Magician", number: 1, meaning: "Power, skill, concentration, action" },
{ name: "The High Priestess", number: 2, meaning: "Intuition, mystery, subconscious mind" },
{ name: "The Empress", number: 3, meaning: "Fertility, nature, abundance, sensuality" },
{ name: "The Emperor", number: 4, meaning: "Authority, structure, control, fatherhood" },
{ name: "The Hierophant", number: 5, meaning: "Tradition, conformity, morality, ethics" },
{ name: "The Lovers", number: 6, meaning: "Love, harmony, relationships, values alignment" },
{ name: "The Chariot", number: 7, meaning: "Control, willpower, success, determination" },
{ name: "Strength", number: 8, meaning: "Strength, courage, persuasion, compassion" },
{ name: "The Hermit", number: 9, meaning: "Soul-searching, introspection, being alone" },
{ name: "Wheel of Fortune", number: 10, meaning: "Good luck, karma, life cycles, destiny" },
{ name: "Justice", number: 11, meaning: "Justice, fairness, truth, cause and effect" },
{ name: "The Hanged Man", number: 12, meaning: "Pause, surrender, letting go, new perspectives" },
{ name: "Death", number: 13, meaning: "Endings, change, transformation, transition" },
{ name: "Temperance", number: 14, meaning: "Balance, moderation, patience, purpose" },
{ name: "The Devil", number: 15, meaning: "Shadow self, attachment, addiction, restriction" },
{ name: "The Tower", number: 16, meaning: "Disaster, upheaval, sudden change, revelation" },
{ name: "The Star", number: 17, meaning: "Hope, faith, purpose, renewal, spirituality" },
{ name: "The Moon", number: 18, meaning: "Illusion, fear, anxiety, subconscious, intuition" },
{ name: "The Sun", number: 19, meaning: "Positivity, fun, warmth, success, vitality" },
{ name: "Judgment", number: 20, meaning: "Judgment, rebirth, inner calling, absolution" },
{ name: "The World", number: 21, meaning: "Completion, integration, accomplishment, travel" }
];
const result = await app.prompt("Tarot Card Draw", {
inputs: [
{
label: "Question or Focus (optional)",
type: "text",
placeholder: "What do you seek guidance on?"
},
{
label: "Spread Type",
type: "select",
options: [
{ label: "Single Card", value: "single" },
{ label: "Three Card (Past/Present/Future)", value: "three" },
{ label: "Celtic Cross (10 Cards)", value: "celtic" }
],
value: "single"
},
{
label: "Allow Reversed Cards",
type: "checkbox",
value: true
}
]
});
if (result) {
let shuffleDeck = function() {
const deck = [...majorArcana];
for (let i = deck.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[deck[i], deck[j]] = [deck[j], deck[i]];
}
return deck;
}, drawCards = function(count, reversed) {
const deck = shuffleDeck();
const drawn = [];
for (let i = 0; i < count; i++) {
const card = deck[i];
const isReversed = reversed ? Math.random() < 0.5 : false;
drawn.push({
...card,
reversed: isReversed,
fullMeaning: isReversed ? `${card.meaning} (Reversed: opposite or blocked energy)` : card.meaning
});
}
return drawn;
};
const [question, spreadType, allowReversed] = result;
const cardCount = spreadType === "single" ? 1 : spreadType === "three" ? 3 : 10;
const cards = drawCards(cardCount, allowReversed);
let finalResult = `<mark>**Tarot Reading**</mark>
`;
if (question) finalResult += `**Question:** ${question}
`;
if (spreadType === "single") {
const card = cards[0];
finalResult += `**Card:** ${card.name} ${card.reversed ? "(Reversed)" : ""}
`;
finalResult += `**Meaning:** ${card.fullMeaning}
`;
} else if (spreadType === "three") {
const positions = ["Past", "Present", "Future"];
finalResult += `**Three Card Spread**
`;
cards.forEach((card, i) => {
finalResult += `**${positions[i]}:** ${card.name} ${card.reversed ? "(Reversed)" : ""}
`;
finalResult += `*${card.fullMeaning}*
`;
});
} else {
const positions = [
"Present Situation",
"Challenge",
"Past Foundation",
"Recent Past",
"Possible Outcome",
"Near Future",
"Your Approach",
"External Influences",
"Hopes/Fears",
"Final Outcome"
];
finalResult += `**Celtic Cross Spread**
`;
cards.forEach((card, i) => {
finalResult += `**${positions[i]}:** ${card.name} ${card.reversed ? "(Reversed)" : ""}
`;
finalResult += `*${card.fullMeaning}*
`;
});
}
const now = /* @__PURE__ */ new Date();
const YYMMDD = now.toISOString().slice(2, 10).replace(/-/g, "");
const HHMMSS = now.toTimeString().slice(0, 8).replace(/:/g, "");
const auditNoteName = `Dice Results Audit`;
const auditTagName = ["-reports/-dice"];
const auditnoteUUID = await getNoteUUID(app, auditNoteName, auditTagName);
(async () => {
try {
const cardDetails = cards.map((c) => `${c.name}${c.reversed ? " (Reversed)" : ""}: ${c.fullMeaning}`).join(" | ");
const auditReport = `- <mark>Tarot:</mark> ***When:** ${YYMMDD}_${HHMMSS}*; Spread: ${spreadType} | Q: ${question || "N/A"} | <mark>**Results:** ${cardDetails}</mark>`;
await app.insertNoteContent({ uuid: auditnoteUUID }, auditReport);
await app.navigate(`https://www.amplenote.com/notes/${auditnoteUUID}`);
app.alert(finalResult);
} catch (error) {
console.error(error.message);
}
})();
}
}
 
// anp-19-dice/lib/percentile.js
async function percentile_default(app) {
const result = await app.prompt("Percentile Roll (D100)", {
inputs: [
{
label: "Target Number (1-100, leave blank for open roll)",
type: "string",
placeholder: "e.g., 65"
},
{
label: "Roll Type",
type: "select",
options: [
{ label: "Standard (00-99)", value: "standard" },
{ label: "1-100 (00 = 100)", value: "one_to_hundred" },
{ label: "Flip to Succeed (CoC style)", value: "flip" }
],
value: "one_to_hundred"
}
]
});
if (result) {
let rollD100 = function(type) {
const tens = Math.floor(Math.random() * 10) * 10;
const ones = Math.floor(Math.random() * 10);
if (type === "standard") {
return tens + ones;
} else if (type === "one_to_hundred") {
return tens === 0 && ones === 0 ? 100 : tens + ones;
} else if (type === "flip") {
const roll2 = tens + ones;
const effectiveRoll = roll2 === 0 ? 100 : roll2;
return {
roll: effectiveRoll,
tensDigit: Math.floor(tens / 10),
onesDigit: ones,
flipped: Math.floor(tens / 10) * 1 + ones * 10
};
}
};
const [targetStr, rollType] = result;
const target = parseInt(targetStr);
let finalResult = `<mark>**Percentile Roll**</mark>
`;
let roll;
if (rollType === "flip" && target) {
roll = rollD100("flip");
const normalSuccess = roll.roll <= target;
const flipSuccess = roll.flipped <= target;
finalResult += `**Tens Die:** ${roll.tensDigit} | **Ones Die:** ${roll.onesDigit}
`;
finalResult += `**Roll:** ${roll.roll} (Target: <=${target})
`;
finalResult += `**Flipped Roll:** ${roll.flipped}
`;
if (normalSuccess && flipSuccess) {
finalResult += `**Result:** Critical Success! (Both rolls succeed)`;
} else if (normalSuccess || flipSuccess) {
finalResult += `**Result:** Regular Success (One roll succeeds)`;
} else {
finalResult += `**Result:** Failure`;
}
} else {
roll = rollD100(rollType);
finalResult += `**Roll:** ${roll}
`;
if (target) {
const success = rollType === "standard" ? roll < target : roll <= target;
finalResult += `**Target:** <=${target}
`;
finalResult += `**Result:** ${success ? "Success! \u2713" : "Failure \u2717"}
`;
if (rollType === "one_to_hundred") {
if (roll === 1) finalResult += `**Critical Success!** \u{1F3AF}
`;
if (roll === 100) finalResult += `**Fumble!** \u{1F4A5}
`;
if (roll === target) finalResult += `**Exactly target!** \u{1F44C}
`;
}
}
}
finalResult += `**Roll Type:** ${rollType}`;
const now = /* @__PURE__ */ new Date();
const YYMMDD = now.toISOString().slice(2, 10).replace(/-/g, "");
const HHMMSS = now.toTimeString().slice(0, 8).replace(/:/g, "");
const auditNoteName = `Dice Results Audit`;
const auditTagName = ["-reports/-dice"];
const auditnoteUUID = await getNoteUUID(app, auditNoteName, auditTagName);
(async () => {
try {
const rollValue = typeof roll === "object" ? roll.roll : roll;
const success = target ? rollValue <= target ? "Success" : "Fail" : "Open";
const auditReport = `- <mark>Percentile:</mark> ***When:** ${YYMMDD}_${HHMMSS}*; <mark>**Roll:** ${rollValue}, **Target:** ${target || "Open"}, **Result:** ${success}</mark> | Type: ${rollType}`;
await app.insertNoteContent({ uuid: auditnoteUUID }, auditReport);
await app.navigate(`https://www.amplenote.com/notes/${auditnoteUUID}`);
app.alert(finalResult);
} catch (error) {
console.error(error.message);
}
})();
}
}
 
// anp-19-dice/dice.js
function wrapFeature(featureName, moduleFunc) {
return async function(app, ...args) {
try {
const statsStr = await app.settings["Dice_Usage_Stats"];
let stats = {};
try {
stats = statsStr ? JSON.parse(statsStr) : {};
} catch (e) {
}
stats[featureName] = (stats[featureName] || 0) + 1;
await app.setSetting("Dice_Usage_Stats", JSON.stringify(stats));
const showAlertVal = await app.settings["Dice_Show_Alert"];
const showAlert = showAlertVal === "true" || showAlertVal === true;
let auditUUID = await app.settings["Dice_Audit_UUID [Do not Edit!]"];
const originalSetSetting = app.setSetting ? app.setSetting.bind(app) : null;
app.setSetting = async function(key, value) {
if (key === "Dice_Audit_UUID [Do not Edit!]") {
auditUUID = value;
}
if (originalSetSetting) {
return await originalSetSetting(key, value);
}
};
let resolveAudit;
const auditPromise = new Promise((resolve) => {
resolveAudit = resolve;
});
let promptCancelled = false;
const originalPrompt = app.prompt ? app.prompt.bind(app) : null;
app.prompt = async function(title, options) {
const res = originalPrompt ? await originalPrompt(title, options) : null;
if (!res) {
promptCancelled = true;
}
return res;
};
const originalInsertNoteContent = app.insertNoteContent ? app.insertNoteContent.bind(app) : null;
let capturedContent = "";
app.insertNoteContent = async function(target, content, options) {
if (auditUUID && target && target.uuid === auditUUID) {
capturedContent = content;
resolveAudit(content);
}
if (originalInsertNoteContent) {
return await originalInsertNoteContent(target, content, options);
}
};
const originalNavigate = app.navigate ? app.navigate.bind(app) : null;
app.navigate = async function(url) {
if (featureName !== "View Roll History" && auditUUID && url.includes(auditUUID)) {
return;
}
if (originalNavigate) {
return await originalNavigate(url);
}
};
let stopWaiting = featureName === "View Roll History" || featureName === "Clear Audit History";
let alertCalled = false;
const originalAlert = app.alert ? app.alert.bind(app) : null;
app.alert = function(message) {
alertCalled = true;
const isErrorOrValidation = (msg) => {
if (!msg) return false;
const lower = msg.toLowerCase();
return lower.includes("error") || lower.includes("invalid") || lower.includes("please") || lower.includes("must be") || lower.includes("warning") || lower.includes("need at least") || lower.includes("no notes found") || lower.includes("not found");
};
if (isErrorOrValidation(message)) {
stopWaiting = true;
}
if (showAlert || isErrorOrValidation(message) || featureName === "View Roll History" || featureName === "Clear Audit History") {
if (originalAlert) {
return originalAlert(stripMarkdown(message));
}
}
};
await moduleFunc(app, ...args);
if (!promptCancelled && !stopWaiting) {
await Promise.race([
auditPromise,
new Promise((resolve) => setTimeout(resolve, 5e3))
]);
}
if (capturedContent) {
let cleaned = capturedContent;
if (showAlert && !alertCalled && originalAlert) {
originalAlert(stripMarkdown(cleaned));
}
if (featureName === "Table - Randomizer" && args[0]) {
const noteUUID = args[0];
if (originalInsertNoteContent) {
await originalInsertNoteContent({ uuid: noteUUID }, cleaned, { atEnd: false });
}
} else {
if (app.context && typeof app.context.replaceSelection === "function") {
try {
const success = await app.context.replaceSelection(cleaned);
if (success) {
return null;
}
} catch (e) {
console.warn("replaceSelection failed:", e);
}
}
return cleaned;
}
}
} catch (error) {
console.error(`Plugin Error in [${featureName}]:`, error);
if (app.alert) {
app.alert(`An error occurred in ${featureName}:
${error.message}`);
}
}
};
}
var dice_default = {
insertText: {
// General Dice
"Basic": wrapFeature("Basic", basic_default),
"Advanced": wrapFeature("Advanced", advanced_default),
"Quick Roll Presets": wrapFeature("Quick Roll Presets", quick_presets_default),
"Percentile (D100)": wrapFeature("Percentile (D100)", percentile_default),
// Game Systems
"Fudge/Fate": wrapFeature("Fudge/Fate", fudge_fate_default),
"Fantasy AGE Stunt - Single Roll": wrapFeature("Fantasy AGE Stunt - Single Roll", fantasy_age_stunt_single_roll_default),
"Fantasy AGE Stunt - Roll All At Once": wrapFeature("Fantasy AGE Stunt - Roll All At Once", fantasy_age_stunt_roll_all_at_once_default),
"Dice Pool (Shadowrun/WoD)": wrapFeature("Dice Pool (Shadowrun/WoD)", dice_pool_default),
// Oracles & Divination
"Specialized": wrapFeature("Specialized", specialized_default),
"8 Ball": wrapFeature("8 Ball", ball_default),
"Ask Sai Baba": wrapFeature("Ask Sai Baba", ask_sai_baba_default),
"Tarot Cards": wrapFeature("Tarot Cards", tarot_default),
// Generators & Tools
"Weighted Random": wrapFeature("Weighted Random", weighted_random_default),
"Randomized Decision Matrix": wrapFeature("Randomized Decision Matrix", decision_matrix_default),
"Name Generator": wrapFeature("Name Generator", name_generator_default),
// History
"View Roll History": wrapFeature("View Roll History", viewRollHistory),
"Clear Audit History": wrapFeature("Clear Audit History", clearAuditHistory)
},
noteOption: {
"Table - Randomizer": wrapFeature("Table - Randomizer", table_randomizer_default)
}
};
 
 
return dice_default;
})()