Compare commits
4 Commits
269a844a68
...
3656fc42ac
Author | SHA1 | Date | |
---|---|---|---|
3656fc42ac | |||
7839dbbc1c | |||
e6d8583220 | |||
f8fa3ec924 |
89
lib/dice.ts
Normal file
89
lib/dice.ts
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
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,6 +1,4 @@
|
|||||||
type QueryableObject = Record<string, any>;
|
export class TTCQueryParser {
|
||||||
|
|
||||||
class TtcQueryParser {
|
|
||||||
private data: QueryableObject;
|
private data: QueryableObject;
|
||||||
private relativeMap: Map<QueryableObject, QueryableObject>;
|
private relativeMap: Map<QueryableObject, QueryableObject>;
|
||||||
|
|
||||||
@ -26,24 +24,12 @@ class TtcQueryParser {
|
|||||||
} else if (query.startsWith("$")) {
|
} else if (query.startsWith("$")) {
|
||||||
result = this.queryRelativeObject(query, currentObject);
|
result = this.queryRelativeObject(query, currentObject);
|
||||||
} else {
|
} else {
|
||||||
result = this.queryPublication(query);
|
result = this.searchInObject(this.data, 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
|
||||||
@ -109,7 +95,9 @@ class TtcQueryParser {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
current = current.map((e: any) => e[key]);
|
current = Array.isArray(current)
|
||||||
|
? current.map((e: any) => e[key])
|
||||||
|
: current[key];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return [];
|
return [];
|
||||||
@ -161,17 +149,11 @@ class TtcQueryParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Example usage:
|
// Example usage:
|
||||||
const data = {
|
// const
|
||||||
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
|
||||||
|
117
lib/ttcQuery/TTCResolvers.ts
Normal file
117
lib/ttcQuery/TTCResolvers.ts
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
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,7 +26,9 @@ updated: March 14th, 2024
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# What even is ttcMD?
|
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?
|
||||||
|
|
||||||
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,11 +1,4 @@
|
|||||||
|
|
||||||
1. hello
|
|
||||||
2. everybody
|
|
||||||
3. my
|
|
||||||
4. name
|
|
||||||
5. is
|
|
||||||
6. welcome
|
|
||||||
|
|
||||||
- hello
|
- hello
|
||||||
- everybody
|
- everybody
|
||||||
- yes you
|
- yes you
|
||||||
@ -13,3 +6,10 @@
|
|||||||
- 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,6 +88,9 @@ 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