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
|
||||
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",
|
||||
|
@ -1,6 +1,4 @@
|
||||
type QueryableObject = Record<string, any>;
|
||||
|
||||
class TtcQueryParser {
|
||||
export class TTCQueryParser {
|
||||
private data: QueryableObject;
|
||||
private relativeMap: Map<QueryableObject, QueryableObject>;
|
||||
|
||||
@ -26,24 +24,12 @@ class TtcQueryParser {
|
||||
} else if (query.startsWith("$")) {
|
||||
result = this.queryRelativeObject(query, currentObject);
|
||||
} else {
|
||||
result = this.queryPublication(query);
|
||||
result = this.searchInObject(this.data, 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
|
||||
@ -109,7 +95,9 @@ class TtcQueryParser {
|
||||
return [];
|
||||
}
|
||||
} else {
|
||||
current = current.map((e: any) => e[key]);
|
||||
current = Array.isArray(current)
|
||||
? current.map((e: any) => e[key])
|
||||
: current[key];
|
||||
}
|
||||
} else {
|
||||
return [];
|
||||
@ -161,17 +149,11 @@ class TtcQueryParser {
|
||||
}
|
||||
|
||||
// Example usage:
|
||||
const data = {
|
||||
relative: true,
|
||||
weapon_abilities: [
|
||||
{ name: "Rapid Fire", body: "Shoot again" },
|
||||
{ name: "Sustained Hits", body: "More damage", relative: true },
|
||||
],
|
||||
};
|
||||
// const
|
||||
|
||||
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
|
||||
|
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)
|
||||
- [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,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.
|
||||
|
||||
|
@ -1,11 +1,4 @@
|
||||
|
||||
1. hello
|
||||
2. everybody
|
||||
3. my
|
||||
4. name
|
||||
5. is
|
||||
6. welcome
|
||||
|
||||
- hello
|
||||
- everybody
|
||||
- yes you
|
||||
@ -13,3 +6,10 @@
|
||||
- name
|
||||
- is
|
||||
- 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 = (
|
||||
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,6 +88,9 @@ 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>;
|
||||
|
Loading…
x
Reference in New Issue
Block a user