// noinspection JSUnresolvedVariable const {$, $a, doAfterLoad} = window.fwdekker; import {CategoryScale, Chart, Filler, LinearScale, LineController, LineElement, PointElement, Tooltip} from "chart.js" Chart.register(CategoryScale, Filler, LineController, LineElement, LinearScale, PointElement, Tooltip); ////// /// /// Helper functions /// ////// const repeat = (value, length) => { const zeroArray = []; for (let i = 0; i < length; i++) zeroArray.push(value); return zeroArray; }; const rangeExclusive = (from, to) => { const rangeArray = []; for (let i = from; i < to; i++) rangeArray.push(i); return rangeArray; }; const rangeInclusive = (from, to) => { const rangeArray = rangeExclusive(from, to); rangeArray.push(to); return rangeArray; }; ////// /// /// Input /// ////// const inputTable = {}; // Functions inputTable.getTable = () => $("#die-settings tbody"); inputTable.dieRowCount = () => $a(".die-eyes", inputTable.getTable()).length; inputTable.highestDieRowIndex = () => { const table = inputTable.getTable(); let highestDieRowIndex = -1; $a("tr", table).forEach((node) => { if ("index" in node.dataset) highestDieRowIndex = Math.max(highestDieRowIndex, +node.dataset.index); }); return highestDieRowIndex; }; inputTable.addDieRow = () => { const createNumberInput = (index, className, value) => { const input = document.createElement("input"); input.id = className + index; input.className = className; input.type = "number"; input.min = "1"; input.step = "1"; input.value = value; input.addEventListener("keypress", (e) => { if (e.key === "Enter") outputChart.updateProbGraph(); }); return input; }; const createRemoveLink = (index, className) => { const link = document.createElement("button"); link.id = className + index; link.className = className + " outline"; link.innerHTML = "Remove"; link.addEventListener("click", () => inputTable.removeDieRow(index)); return link; }; const table = inputTable.getTable(); const newIndex = inputTable.highestDieRowIndex() + 1; const row = table.insertRow(inputTable.dieRowCount()); row.dataset.index = "" + newIndex; row.insertCell().appendChild(createNumberInput(newIndex, "die-eyes", 6)); row.insertCell().appendChild(createNumberInput(newIndex, "die-count", 2)); row.insertCell().appendChild(createRemoveLink(newIndex, "die-remove")); if (inputTable.dieRowCount() === 1) $(".die-remove").disabled = true; else $a(".die-remove").forEach((button) => button.disabled = false); $("#die-eyes" + newIndex).focus(); }; inputTable.removeDieRow = index => { if (inputTable.dieRowCount() > 1) $(`tr[data-index="${index}"]`, inputTable.getTable()).remove(); if (inputTable.dieRowCount() === 1) $(".die-remove").disabled = true; }; inputTable.getDice = () => { const dice = []; const countInputs = $a(".die-count"); const eyesInputs = $a(".die-eyes"); for (let i = 0; i < eyesInputs.length; i++) { const count = parseInt(countInputs.item(i).value); const eyes = parseInt(eyesInputs.item(i).value); for (let j = 0; j < count; j++) dice.push(eyes); } return dice; }; // Init doAfterLoad(() => { const button = $("#add-die-row-button"); button.addEventListener("click", () => inputTable.addDieRow()); inputTable.addDieRow(); }); ////// /// /// Output /// ////// const outputChart = {}; let probChart; // Functions outputChart.calculateDiceFrequencies = dice => { if (dice.length === 0) return []; // Roll dice let rollFreqs = [0].concat(repeat(1, dice[0])); dice.slice(1).forEach(die => { const dieRollFreqs = rollFreqs.concat(repeat(0, die)); rollFreqs = repeat(0, dieRollFreqs.length); rangeInclusive(1, die).forEach(rollValue => { rangeExclusive(1, dieRollFreqs.length - die).forEach(i => { rollFreqs[rollValue + i] += dieRollFreqs[i]; }); }); }); rollFreqs.shift(); // Calculate frequencies const totalRolls = rollFreqs.reduce((a, b) => a + b, 0); rangeExclusive(0, rollFreqs.length).forEach(roll => rollFreqs[roll] = rollFreqs[roll] / totalRolls); return rollFreqs; }; outputChart.updateProbGraph = () => { const dice = inputTable.getDice(); const rollFreqs = outputChart.calculateDiceFrequencies(dice); probChart.data.labels = rangeInclusive(1, rollFreqs.length); probChart.data.datasets = [{ data: rollFreqs, backgroundColor: "rgb(0, 51, 204, 0.4)" }]; probChart.update(); }; // Init doAfterLoad(() => { probChart = new Chart( $("#prob-chart").getContext("2d"), { type: "line", data: {}, options: {fill: true}, } ); $("#inputs").addEventListener("submit", (event) => { event.preventDefault(); outputChart.updateProbGraph(); }); outputChart.updateProbGraph(); });