Compare commits

..

No commits in common. "3656fc42acf6b4dbd659070d2f0e2ae918555b3f" and "269a844a6833b0787ab83c0fe7ad83110c322e5a" have entirely different histories.

7 changed files with 43 additions and 236 deletions

View File

@ -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);
}
}

View File

@ -197,7 +197,7 @@ export const buildOnlyDefaultElements = () => {
// list
registerIdentifier(
"list",
/(?<=\n\n?|^) *-\s([\s\S]*?)(?=\n\n|$)/g,
/(?<=\n\n|^) *-\s([\s\S]*?)(?=\n\n|$)/g,
(s, rx) => {
return {
content: s.match(new RegExp(rx, ""))?.at(0) || "Unable to parse list",

View File

@ -1,4 +1,6 @@
export class TTCQueryParser {
type QueryableObject = Record<string, any>;
class TtcQueryParser {
private data: QueryableObject;
private relativeMap: Map<QueryableObject, QueryableObject>;
@ -24,12 +26,24 @@ export class TTCQueryParser {
} else if (query.startsWith("$")) {
result = this.queryRelativeObject(query, currentObject);
} else {
result = this.searchInObject(this.data, query);
result = this.queryPublication(query);
}
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(
query: string,
currentObject: QueryableObject
@ -95,9 +109,7 @@ export class TTCQueryParser {
return [];
}
} else {
current = Array.isArray(current)
? current.map((e: any) => e[key])
: current[key];
current = current.map((e: any) => e[key]);
}
} else {
return [];
@ -149,11 +161,17 @@ export class TTCQueryParser {
}
// 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
// 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].body", data)); // Example output
// Example queries
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].body", data)); // Example output

View File

@ -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;
}
}

View File

@ -7,7 +7,7 @@ updated: March 14th, 2024
# 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)
- [Links](#links)
- [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.

View File

@ -1,4 +1,11 @@
1. hello
2. everybody
3. my
4. name
5. is
6. welcome
- hello
- everybody
- yes you
@ -6,10 +13,3 @@
- name
- is
- welcome
1. hello
2. everybody
3. my
4. name
5. is
6. welcome

9
types.d.ts vendored
View File

@ -31,7 +31,7 @@ type FrontMatter = Record<string, string>;
type SearchFunction = (
s: string,
start: number,
end: number
end: number,
) => {
start: number;
end: number;
@ -53,7 +53,7 @@ type IdentifierRegistration = <N = Record<string, string>>(
parseFunction: (s: string, rx: RegExp) => IdentifiedToken<N>,
renderFunction: TokenRenderer<N>,
openTagRx?: RegExp,
closeTagRx?: RegExp
closeTagRx?: RegExp,
) => void;
// Schema
@ -88,9 +88,6 @@ type InputBinder = {
name: string;
value: string | number;
onChange: (
e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>
e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>,
) => void;
};
// Query
type QueryableObject = Record<string, any>;