Compare commits
No commits in common. "3656fc42acf6b4dbd659070d2f0e2ae918555b3f" and "269a844a6833b0787ab83c0fe7ad83110c322e5a" have entirely different histories.
3656fc42ac
...
269a844a68
89
lib/dice.ts
89
lib/dice.ts
@ -1,89 +0,0 @@
|
|||||||
export class Dice {
|
|
||||||
private count!: number;
|
|
||||||
private sides!: number;
|
|
||||||
|
|
||||||
constructor(dice: string) {
|
|
||||||
this.parseDice(dice);
|
|
||||||
}
|
|
||||||
|
|
||||||
private parseDice(dice: string) {
|
|
||||||
const [c, s] = dice.split(/[dD]/);
|
|
||||||
this.count = Number(c);
|
|
||||||
this.sides = Number(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
public roll() {
|
|
||||||
let total = 0;
|
|
||||||
for (let i = 0; i < this.count; i++) {
|
|
||||||
total += this.rollSingle();
|
|
||||||
}
|
|
||||||
return total;
|
|
||||||
}
|
|
||||||
|
|
||||||
private rollSingle() {
|
|
||||||
return Math.ceil(Math.random() * this.sides);
|
|
||||||
}
|
|
||||||
|
|
||||||
public rollAvg() {
|
|
||||||
return this.roll() / this.count;
|
|
||||||
}
|
|
||||||
|
|
||||||
public rollTimes(times: number) {
|
|
||||||
let total = 0;
|
|
||||||
for (let i = 0; i < times; i++) {
|
|
||||||
total += this.roll();
|
|
||||||
}
|
|
||||||
return total;
|
|
||||||
}
|
|
||||||
|
|
||||||
public rollTimesAvg(times: number) {
|
|
||||||
return this.rollTimes(times) / times;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getNormalizedRollDistribution(): Record<number, number> {
|
|
||||||
const distribution: Record<number, number> = this.computeDistribution();
|
|
||||||
|
|
||||||
// Normalize the distribution
|
|
||||||
const totalOutcomes = Math.pow(this.sides, this.count);
|
|
||||||
for (const sum in distribution) {
|
|
||||||
if (distribution.hasOwnProperty(sum)) {
|
|
||||||
distribution[sum] /= totalOutcomes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return distribution;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getRollDistribution(): Record<number, number> {
|
|
||||||
return this.computeDistribution();
|
|
||||||
}
|
|
||||||
|
|
||||||
private computeDistribution(): Record<number, number> {
|
|
||||||
const distribution: Record<number, number> = {};
|
|
||||||
|
|
||||||
// Helper function to compute the sum distribution for given number of dice
|
|
||||||
const computeSumDistribution = (
|
|
||||||
dice: number,
|
|
||||||
sides: number,
|
|
||||||
currentSum: number,
|
|
||||||
currentDice: number
|
|
||||||
): void => {
|
|
||||||
if (currentDice === dice) {
|
|
||||||
distribution[currentSum] = (distribution[currentSum] || 0) + 1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (let i = 1; i <= sides; i++) {
|
|
||||||
computeSumDistribution(dice, sides, currentSum + i, currentDice + 1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Compute distribution
|
|
||||||
computeSumDistribution(this.count, this.sides, 0, 0);
|
|
||||||
|
|
||||||
return distribution;
|
|
||||||
}
|
|
||||||
// STATIC
|
|
||||||
static isDice(d: string) {
|
|
||||||
return /\d+[dD]\d+/.test(d);
|
|
||||||
}
|
|
||||||
}
|
|
@ -197,7 +197,7 @@ export const buildOnlyDefaultElements = () => {
|
|||||||
// list
|
// list
|
||||||
registerIdentifier(
|
registerIdentifier(
|
||||||
"list",
|
"list",
|
||||||
/(?<=\n\n?|^) *-\s([\s\S]*?)(?=\n\n|$)/g,
|
/(?<=\n\n|^) *-\s([\s\S]*?)(?=\n\n|$)/g,
|
||||||
(s, rx) => {
|
(s, rx) => {
|
||||||
return {
|
return {
|
||||||
content: s.match(new RegExp(rx, ""))?.at(0) || "Unable to parse list",
|
content: s.match(new RegExp(rx, ""))?.at(0) || "Unable to parse list",
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
export class TTCQueryParser {
|
type QueryableObject = Record<string, any>;
|
||||||
|
|
||||||
|
class TtcQueryParser {
|
||||||
private data: QueryableObject;
|
private data: QueryableObject;
|
||||||
private relativeMap: Map<QueryableObject, QueryableObject>;
|
private relativeMap: Map<QueryableObject, QueryableObject>;
|
||||||
|
|
||||||
@ -24,12 +26,24 @@ export class TTCQueryParser {
|
|||||||
} else if (query.startsWith("$")) {
|
} else if (query.startsWith("$")) {
|
||||||
result = this.queryRelativeObject(query, currentObject);
|
result = this.queryRelativeObject(query, currentObject);
|
||||||
} else {
|
} else {
|
||||||
result = this.searchInObject(this.data, query);
|
result = this.queryPublication(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private queryPublication(query: string): any[] {
|
||||||
|
// Example implementation for searching publication
|
||||||
|
const publicationMatch = query.match(/^(\w+)(\[(\w+)\])?(\..+)?/);
|
||||||
|
if (publicationMatch) {
|
||||||
|
const subQuery = publicationMatch[4];
|
||||||
|
|
||||||
|
// Search within the publication data
|
||||||
|
return this.searchInObject(this.data, subQuery);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
private queryCurrentObject(
|
private queryCurrentObject(
|
||||||
query: string,
|
query: string,
|
||||||
currentObject: QueryableObject
|
currentObject: QueryableObject
|
||||||
@ -95,9 +109,7 @@ export class TTCQueryParser {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
current = Array.isArray(current)
|
current = current.map((e: any) => e[key]);
|
||||||
? current.map((e: any) => e[key])
|
|
||||||
: current[key];
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return [];
|
return [];
|
||||||
@ -149,11 +161,17 @@ export class TTCQueryParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Example usage:
|
// Example usage:
|
||||||
// const
|
const data = {
|
||||||
|
relative: true,
|
||||||
|
weapon_abilities: [
|
||||||
|
{ name: "Rapid Fire", body: "Shoot again" },
|
||||||
|
{ name: "Sustained Hits", body: "More damage", relative: true },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
// const parser = new TTCQueryParser(data);
|
const parser = new TtcQueryParser(data);
|
||||||
|
|
||||||
// // Example queries
|
// Example queries
|
||||||
// console.log(parser.search("$weapon_abilities[name=Rapid Fire].body")); // Example output
|
console.log(parser.search("weapon_abilities[name=Rapid Fire].body")); // Example output
|
||||||
// // console.log(parser.search("^weapon_abilities[name=Rapid Fire]", data)); // Example output
|
// console.log(parser.search("^weapon_abilities[name=Rapid Fire]", data)); // Example output
|
||||||
// console.log(parser.search("$weapon_abilities[name=Rapid Fire].body", data)); // Example output
|
console.log(parser.search("$weapon_abilities[name=Rapid Fire].body", data)); // Example output
|
||||||
|
@ -1,117 +0,0 @@
|
|||||||
import { Dice } from "../dice";
|
|
||||||
import { TTCQueryParser } from "./TTCQueryParser";
|
|
||||||
|
|
||||||
export class TTCQueryResolver {
|
|
||||||
private parser: TTCQueryParser | null;
|
|
||||||
private context: QueryableObject | null = null;
|
|
||||||
|
|
||||||
private stack: any[] = [];
|
|
||||||
|
|
||||||
constructor(parser?: TTCQueryParser) {
|
|
||||||
this.parser = parser || null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public setParser(parser: TTCQueryParser) {
|
|
||||||
this.parser = parser;
|
|
||||||
}
|
|
||||||
|
|
||||||
public setContext(obj: QueryableObject) {
|
|
||||||
this.context = obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
public resolve(resolver: string, onDemand?: boolean) {
|
|
||||||
const resList = resolver.split(",");
|
|
||||||
for (const res of resList) {
|
|
||||||
this.stack.push(this.parseResolver(res));
|
|
||||||
}
|
|
||||||
const last = this.stack.at(-1);
|
|
||||||
if (typeof last === "function" && !onDemand) return last();
|
|
||||||
return last;
|
|
||||||
}
|
|
||||||
|
|
||||||
private parseResolver(resolver: string) {
|
|
||||||
if (this.isArithmetic(resolver)) return this.solveArithmetic(resolver);
|
|
||||||
if (this.isQuery(resolver)) this.runQuery(resolver);
|
|
||||||
}
|
|
||||||
private isQuery(resolver: string) {
|
|
||||||
return (
|
|
||||||
resolver.startsWith("?") ||
|
|
||||||
resolver.startsWith("_") ||
|
|
||||||
/^\$\d\./.test(resolver)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
private runQuery(query: string) {
|
|
||||||
if (!this.parser) throw "Parser not defined in query resolver";
|
|
||||||
|
|
||||||
if (query.startsWith("$")) {
|
|
||||||
const [_, idx, q] = query.match(/^(\$\d+)\.(.*)/) || [];
|
|
||||||
if (!_) throw "Detected stack query but did not match the regex";
|
|
||||||
const stackItem = this.getFromStack(idx);
|
|
||||||
if (typeof stackItem === "string" && Dice.isDice(stackItem)) {
|
|
||||||
return this.handleDice(stackItem, q);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.parser.search(q);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if (query.startsWith("?") || query.startsWith("_"))
|
|
||||||
return query.startsWith("_") && this.context
|
|
||||||
? this.parser.search(query.replace("_", ""), this.context)
|
|
||||||
: this.parser.search(query.replace(/^[?_].?/, ""));
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleDice(dice: string, query: string) {
|
|
||||||
const d = new Dice(dice);
|
|
||||||
const [method, n] = query.split(":");
|
|
||||||
let num = Number(n);
|
|
||||||
if (n.startsWith("$")) num = this.getFromStack(n);
|
|
||||||
switch (method) {
|
|
||||||
case "roll":
|
|
||||||
return () => d.roll();
|
|
||||||
case "rollAvg":
|
|
||||||
return () => d.rollAvg();
|
|
||||||
case "rollTimes":
|
|
||||||
return () => d.rollTimes(num);
|
|
||||||
case "rollTimesAvg":
|
|
||||||
return () => d.rollTimesAvg(num);
|
|
||||||
default:
|
|
||||||
return "No valid method provided for dice";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private isArithmetic(resolver: string) {
|
|
||||||
return resolver.split(/\+|\/|-|\*|\^/).filter((e) => !!e).length > 1;
|
|
||||||
}
|
|
||||||
private solveArithmetic(resolver: string) {
|
|
||||||
const [n1, op, n2] = resolver
|
|
||||||
.match(/(\$?\d+)([+\-*\/^])(\$?\d+)/)
|
|
||||||
?.slice(1) || ["", "+", ""];
|
|
||||||
let num1 = Number(n1),
|
|
||||||
num2 = Number(n2);
|
|
||||||
|
|
||||||
if (n1.startsWith("$")) num1 = this.getFromStack<number>(n1);
|
|
||||||
if (n2.startsWith("$")) num2 = this.getFromStack<number>(n2);
|
|
||||||
console.log(num1, num2);
|
|
||||||
|
|
||||||
switch (op) {
|
|
||||||
case "+":
|
|
||||||
return num1 + num2;
|
|
||||||
case "-":
|
|
||||||
return num1 - num2;
|
|
||||||
case "*":
|
|
||||||
return num1 * num2;
|
|
||||||
case "/":
|
|
||||||
return num1 / num2;
|
|
||||||
case "^":
|
|
||||||
return num1 ^ num2;
|
|
||||||
default:
|
|
||||||
throw "Arithmetic detected but no proper operator assigned";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private getFromStack<T>(stackIndex: string): T {
|
|
||||||
const i = Number(stackIndex.replace("$", ""));
|
|
||||||
const val = this.stack[i] as T;
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,7 +7,7 @@ updated: March 14th, 2024
|
|||||||
|
|
||||||
# Table of Contents
|
# Table of Contents
|
||||||
- [Table of Contents](#table-of-contents)
|
- [Table of Contents](#table-of-contents)
|
||||||
- [What even is ttcMD?](#what-even-is-ttcmd)
|
- [What even is ttcMD?](#what-even-is-ttcmd)
|
||||||
- [Enhanced Standard Elements](#enhanced-standard-elements)
|
- [Enhanced Standard Elements](#enhanced-standard-elements)
|
||||||
- [Links](#links)
|
- [Links](#links)
|
||||||
- [Tables](#tables)
|
- [Tables](#tables)
|
||||||
@ -26,9 +26,7 @@ updated: March 14th, 2024
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
This help article contains a lot of examples of how to use the syntax of ttcMD and what they look like when put into practice. It's very information dense and also a bit chaotic with all of the examples, so please feel free to use the table of contents to find the specific section you are looking for.
|
# What even is ttcMD?
|
||||||
|
|
||||||
## What even is ttcMD?
|
|
||||||
|
|
||||||
ttcMD is a flavor of markdown that has been specifically designed to use with [ttcQuery](/help/ttcQuery.md). It has all of the basic syntax of [markdown](https://www.markdownguide.org/cheat-sheet/), but also includes Tables, basic Fenced Code Blocks and a slew of custom elements and styling annotations.
|
ttcMD is a flavor of markdown that has been specifically designed to use with [ttcQuery](/help/ttcQuery.md). It has all of the basic syntax of [markdown](https://www.markdownguide.org/cheat-sheet/), but also includes Tables, basic Fenced Code Blocks and a slew of custom elements and styling annotations.
|
||||||
|
|
||||||
|
@ -1,4 +1,11 @@
|
|||||||
|
|
||||||
|
1. hello
|
||||||
|
2. everybody
|
||||||
|
3. my
|
||||||
|
4. name
|
||||||
|
5. is
|
||||||
|
6. welcome
|
||||||
|
|
||||||
- hello
|
- hello
|
||||||
- everybody
|
- everybody
|
||||||
- yes you
|
- yes you
|
||||||
@ -6,10 +13,3 @@
|
|||||||
- name
|
- name
|
||||||
- is
|
- is
|
||||||
- welcome
|
- welcome
|
||||||
|
|
||||||
1. hello
|
|
||||||
2. everybody
|
|
||||||
3. my
|
|
||||||
4. name
|
|
||||||
5. is
|
|
||||||
6. welcome
|
|
9
types.d.ts
vendored
9
types.d.ts
vendored
@ -31,7 +31,7 @@ type FrontMatter = Record<string, string>;
|
|||||||
type SearchFunction = (
|
type SearchFunction = (
|
||||||
s: string,
|
s: string,
|
||||||
start: number,
|
start: number,
|
||||||
end: number
|
end: number,
|
||||||
) => {
|
) => {
|
||||||
start: number;
|
start: number;
|
||||||
end: number;
|
end: number;
|
||||||
@ -53,7 +53,7 @@ type IdentifierRegistration = <N = Record<string, string>>(
|
|||||||
parseFunction: (s: string, rx: RegExp) => IdentifiedToken<N>,
|
parseFunction: (s: string, rx: RegExp) => IdentifiedToken<N>,
|
||||||
renderFunction: TokenRenderer<N>,
|
renderFunction: TokenRenderer<N>,
|
||||||
openTagRx?: RegExp,
|
openTagRx?: RegExp,
|
||||||
closeTagRx?: RegExp
|
closeTagRx?: RegExp,
|
||||||
) => void;
|
) => void;
|
||||||
|
|
||||||
// Schema
|
// Schema
|
||||||
@ -88,9 +88,6 @@ type InputBinder = {
|
|||||||
name: string;
|
name: string;
|
||||||
value: string | number;
|
value: string | number;
|
||||||
onChange: (
|
onChange: (
|
||||||
e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>
|
e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>,
|
||||||
) => void;
|
) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Query
|
|
||||||
type QueryableObject = Record<string, any>;
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user