Skip to content

Commit

Permalink
Merge branch 'van/sro/formula' into van/sro/optimizeSelector
Browse files Browse the repository at this point in the history
  • Loading branch information
nguyentvan7 authored Jan 2, 2024
2 parents fc3e2db + 8efc5da commit 39e9dad
Show file tree
Hide file tree
Showing 5 changed files with 199 additions and 11 deletions.
8 changes: 4 additions & 4 deletions libs/sr-formula/src/data/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ const data: TagMapNodeEntries = [
reader.withTag({ src: 'iso', et: 'self' }).reread(reader.src('custom')),
reader.withTag({ src: 'agg', et: 'self' }).reread(reader.src('custom')),

// convert src:char, lightCone, relic to src:agg for accumulation
reader.src('agg').add(reader.sum.src('char')),
reader.src('agg').add(reader.sum.src('lightCone')),
reader.src('agg').add(reader.sum.src('relic')),
// convert src:char, lightCone to src:agg for accumulation
// src: relic is reread in src/util.ts:relicsData()
reader.src('agg').reread(reader.src('char')),
reader.src('agg').reread(reader.src('lightCone')),

// Final <= Premod <= Base
reader
Expand Down
51 changes: 50 additions & 1 deletion libs/sr-formula/src/data/util/read.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {
} from './listing'

export const fixedTags = {
presets: presets,
preset: presets,
member: members,
dst: members,
et: entryTypes,
Expand Down Expand Up @@ -117,6 +117,55 @@ export function tagVal(cat: keyof Tag): TagValRead {
return baseTagVal(cat)
}

export function tagStr(tag: Tag, ex?: any): string {
const {
name,
preset,
member,
dst,
et,
src,
q,
qt,
type,
attackType,
...remaining
} = tag

if (Object.keys(remaining).length) console.error(remaining)

let result = '{ ',
includedRequired = false,
includedBar = false
function required(str: string | undefined | null) {
if (!str) return
result += str + ' '
includedRequired = true
}
function optional(str: string | undefined | null) {
if (!str) return
if (includedRequired && !includedBar) {
includedBar = true
result += '| '
}
result += str + ' '
}
required(name && `#${name}`)
required(preset)
required(member)
required(dst && `(${dst})`)
required(src)
required(et)
if (qt && q) required(`${qt}.${q}`)
else if (qt) required(`${qt}.`)
else if (q) required(`.${q}`)

optional(type)
optional(attackType)
required(ex && `[${ex}]`)
return result + '}'
}

export const reader = new Read({}, undefined)
export const usedNames = new Set<string>()
export const usedQ = new Set('_')
148 changes: 143 additions & 5 deletions libs/sr-formula/src/debug.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
import type {
AnyNode,
CalcResult,
ReRead,
TagMapSubsetCache,
} from '@genshin-optimizer/pando'
import { TagMapExactValues, traverse } from '@genshin-optimizer/pando'
import type { Read, Tag } from './data/util'
import type { Calculator } from './calculator'
import {
Calculator as BaseCalculator,
TagMapExactValues,
TagMapKeys,
traverse,
} from '@genshin-optimizer/pando'
import { type Calculator } from './calculator'
import { keys } from './data'
import { tagStr, type Read, type Tag, type TagMapNodeEntry } from './data/util'

const tagKeys = new TagMapKeys(keys)
export const debugKey = Symbol('tagSource')

export function dependencyString(read: Read, calc: Calculator) {
const str = listDependencies(read.tag, calc).map(({ tag, read, reread }) => {
Expand All @@ -30,8 +40,7 @@ export function listDependencies(
const result: { tag: Tag; read: Tag[]; reread: Tag[] }[] = [],
stack: Tag[] = []
/** Stack depth when first encountered the tag, or 0 if already visited */
const tagKeys = calc.keys
const openDepth = new TagMapExactValues<number>(tagKeys.tagLen, {})
const openDepth = new TagMapExactValues<number>(keys.tagLen, {})

function internal(cache: TagMapSubsetCache<AnyNode | ReRead>) {
const tag = cache.tag,
Expand Down Expand Up @@ -81,3 +90,132 @@ export function listDependencies(
internal(calc.nodes.cache(calc.keys).with(tag))
return result
}

export function printEntry({ tag, value }: TagMapNodeEntry): string {
function printNode(node: AnyNode): string {
const { op, tag, br, x } = node
let { ex } = node
if (op === 'const') return JSON.stringify(ex)
if (op === 'read') return `${node}`
if (op === 'subscript') ex = undefined
const args: string[] = []
if (ex) args.push(JSON.stringify(ex))
if (tag) args.push(tagStr(tag))
args.push(...br.map(printNode), ...x.map(printNode))
return `${op}(` + args.join(', ') + ')'
}

if (value.op === 'reread') return tagStr(tag) + ` <- ` + tagStr(value.tag)
return tagStr(tag) + ` <= ` + printNode(value)
}

export type DebugMeta = {
valText: string
text: string
deps: DebugMeta[]
}
export class DebugCalculator extends BaseCalculator<DebugMeta> {
override computeMeta(
n: AnyNode,
val: number | string,
x: (CalcResult<number | string, DebugMeta> | undefined)[],
br: CalcResult<number | string, DebugMeta>[],
tag: Tag | undefined
): DebugMeta {
const result: DebugMeta = {
valText: valStr(val),
text: '',
deps: [],
}
function toStr(
x: CalcResult<number | string, DebugMeta> | undefined
): string {
if (!x) return ''
result.deps.push(...x.meta.deps)
return x.meta.text
}

function valStr(val: number | string): string {
if (typeof val !== 'number') return `"${val}"`
if (Math.round(val) === val) return `${val}`
return val.toFixed(2)
}

const { op, ex, tag: nTag } = n
switch (op) {
case 'const':
result.text = result.valText
break
case 'read': {
const args = x as CalcResult<number | string, DebugMeta>[]
return {
valText: result.valText,
text: tagStr(nTag!, ex),
deps: [
{
valText: result.valText,
text: `expand ${tagStr(nTag!, ex)} (${tagStr(tag!)})`,
deps: args.map(({ meta, entryTag }) => ({
...meta,
text: `${entryTag?.map((tag) => tagStr(tag)).join(' <- ')} <= ${
meta.text
}`,
})),
},
],
}
}
case 'match':
case 'thres':
case 'lookup': {
const chosen = x.find((x) => x)!
result.text = `${op}(${br.map(toStr).join(', ')} => ${toStr(chosen)})`
break
}
case 'subscript': {
const [index] = br
const chosen = valStr(ex[index.val as number]!)
result.text = `subscript(${toStr(index)} => ${chosen})`
break
}
case 'tag':
result.text = `tag(${tagStr(nTag!)}, ${toStr(x[0])})`
break
case 'dtag':
result.text = `dtag(${br
.map((br, i) => `${ex[i]} => ${toStr(br)}`)
.join(', ')}; ${toStr(x[0])})`
break
default: {
const specialArgs = ex ? [JSON.stringify(ex)] : []
const brArgs = br.map(toStr)
const xArgs = x.map(toStr)
const args = [specialArgs, brArgs, xArgs].map((x) => x.join(', '))

result.text = `${op}(` + args.filter((x) => x.length).join('; ') + ')'
}
}
return result
}

debug(node: AnyNode): string[] {
const { meta } = this.compute(node)
const result: string[] = []
const found = new Set<DebugMeta>()

function print(meta: DebugMeta, level: number) {
const indent = Array(2 * level + 1).join(' ')
const line = `${meta.valText} ${meta.text}`

if (found.has(meta)) {
result.push(indent + line + ' (Dup)')
} else {
found.add(meta)
result.push(indent + line)
meta.deps.forEach((dep) => print(dep, level + 1))
}
}
print(meta, 0)
return result
}
}
1 change: 1 addition & 0 deletions libs/sr-formula/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Calculator } from './calculator'
import { keys, values } from './data'
export { Calculator } from './calculator'
export * from './data/util'
export * from './debug'
export * from './util'

export function srCalculatorWithValues(extras: TagMapEntries<number>) {
Expand Down
2 changes: 1 addition & 1 deletion libs/sr-formula/src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export function relicsData(
}
return [
// Opt-in for artifact buffs, instead of enabling it by default to reduce `read` traffic
reader.src('relic').reread(reader.src('relic')),
reader.src('agg').reread(reader.src('relic')),

// Add `src:dyn` between the stat and the buff so that we can `detach` them easily
reader.withTag({ src: 'relic', qt: 'premod' }).reread(reader.src('dyn')),
Expand Down

0 comments on commit 39e9dad

Please sign in to comment.