Skip to content

Commit 16e1e40

Browse files
committed
add: ap-calc script
1 parent b2cd1e8 commit 16e1e40

File tree

3 files changed

+128
-1
lines changed

3 files changed

+128
-1
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,14 @@ pnpm tsx src/welfares/script.ts
3636

3737
Script that lists event welfare Servants that have not yet been released on the EN server.
3838

39+
### AP Calc
40+
41+
```
42+
pnpm ap-calc [-m,--max <num>] [-n,--node <num>] <current-ap> [<current-timer>]
43+
```
44+
45+
Simple AP Calculator that calculates time to Max AP and, if given a node cost via `-n` (or `--node`) time until each possible run can be done. Output is given as a table with time (24h notation) and delta until that point. Max AP is assumed to be `144`, but can be overriden with `-m` (or `--max`). Values for current ap and time until next AP are positional, with current timer being optional.
46+
3947
## Cache
4048

4149
Instead of constantly redownloading the same data the `pnpm prepare-cache` script downloads all needed data for servants and items at once and stores it in `./data/cache`. Data is transformed to match the data structures descriped in [`./src/types`](./src/types), which avoids duplicates and removes attributes that are not used in any scripts.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
"lint:strict": "eslint --max-warnings 0 .",
1717
"typecheck": "tsc",
1818
"prepare-cache": "tsx src/cache/prepare-cache/script.ts",
19-
"search-servant": "tsx src/search-servant/script.ts"
19+
"search-servant": "tsx src/search-servant/script.ts",
20+
"ap-calc": "tsx src/ap-calc/script.ts"
2021
},
2122
"nano-staged": {
2223
"**/*.{js,cjs,mjs,ts,mts,cts,json,md,yaml}": [

src/ap-calc/script.ts

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import spacetime from "spacetime";
2+
import { parseArgs } from "util";
3+
import { log, logger, timer } from "~/utils/logger";
4+
5+
const getTime = timer();
6+
const args = parseArgs({
7+
args: process.argv.slice(2),
8+
options: {
9+
verbose: { type: "boolean", short: "v", default: false },
10+
max: { type: "string", short: "m", default: "144" },
11+
node: { type: "string", short: "n" }
12+
},
13+
allowPositionals: true
14+
});
15+
const usageText = `USAGE: pnpm ap-calc [--max <num>] [--node <num>] <current-ap> [<current-timer>]`;
16+
17+
const timeDiffer = (() => {
18+
const now = spacetime.now().startOf("second");
19+
20+
return function (deltaSeconds: number) {
21+
const then = now.add(deltaSeconds, "second");
22+
const { diff } = then.since(now);
23+
return {
24+
time: then.format("{time-24}:{second-pad}"),
25+
in: [diff.hours, diff.minutes, diff.seconds]
26+
.map(v => v.toString().padStart(2, "0"))
27+
.join(":")
28+
};
29+
};
30+
})();
31+
32+
async function main() {
33+
// DEBUG
34+
if (args.values.verbose) logger.setLogLevel("Debug");
35+
log.debug(args);
36+
37+
// parse args
38+
let apMax = parseInt(args.values.max);
39+
let nodeCost = args.values.node ? parseInt(args.values.node) : undefined;
40+
if (isNaN(apMax) || apMax < 20) {
41+
log.error(
42+
`Could not parse argument for --max '${args.values.max}'. Argument must integer >= 20`
43+
);
44+
log.warn("Using '5' as fallback value for --max");
45+
apMax = 144;
46+
}
47+
if (nodeCost && isNaN(nodeCost) && nodeCost < 1) {
48+
log.error(
49+
`Could not parse argument for --node '${args.values.node}'. Argument must integer > 0`
50+
);
51+
nodeCost = undefined;
52+
}
53+
54+
// parse positionals
55+
const apCurr = parseInt(args.positionals[0] || "");
56+
if (isNaN(apCurr) || apCurr < 0) {
57+
throw new Error(
58+
`Could not parse value for current ap: '${args.positionals[0]}'`
59+
);
60+
}
61+
const timerValue = args.positionals[1] || "";
62+
let timerSeconds = 300;
63+
if (timerValue) {
64+
const match = timerValue.match(/^([0-4]):([0-5][0-9])$/);
65+
if (!match) {
66+
throw new Error(`Could not parse value for current timer: ${timerValue}`);
67+
}
68+
69+
timerSeconds = Number(match[1]) * 60 + Number(match[2]);
70+
}
71+
log.debug({ apCurr, apMax, timerValue, timerSeconds });
72+
73+
const table = new Array<{
74+
ap: number;
75+
title: string;
76+
time: string;
77+
in: string;
78+
}>();
79+
80+
if (nodeCost) {
81+
let runs = 0;
82+
for (let ap = nodeCost; ap <= apMax; ap += nodeCost) {
83+
const deltaAP = ap - apCurr;
84+
const deltaSeconds = (deltaAP - 1) * 300 + timerSeconds;
85+
table.push(
86+
Object.assign({ ap, title: `Run #${++runs}` }, timeDiffer(deltaSeconds))
87+
);
88+
}
89+
}
90+
91+
// handle max AP
92+
const deltaMaxAP = apMax - apCurr;
93+
const deltaMaxSeconds = (deltaMaxAP - 1) * 300 + timerSeconds;
94+
table.push(
95+
Object.assign({ ap: apMax, title: "Max AP" }, timeDiffer(deltaMaxSeconds))
96+
);
97+
98+
// print table
99+
console.table(
100+
table
101+
.filter(run => run.ap >= apCurr)
102+
.reduce(
103+
(obj, { title, ...row }) => {
104+
obj[title] = row;
105+
return obj;
106+
},
107+
{} as Record<string, Omit<(typeof table)[0], "title">>
108+
),
109+
["ap", "time", "in"]
110+
);
111+
}
112+
113+
main()
114+
.then(() => log.success(`Completed in ${getTime()}`))
115+
.catch(e => {
116+
log.info(usageText);
117+
log.fatal(e);
118+
});

0 commit comments

Comments
 (0)