Skip to content

Commit

Permalink
0.10.4 - Update scripts (#49)
Browse files Browse the repository at this point in the history
  • Loading branch information
charredUtensil authored Sep 22, 2024
1 parent 5c2077e commit 899f875
Show file tree
Hide file tree
Showing 9 changed files with 105 additions and 56 deletions.
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ If you have a clever idea for getting the files from the Downloads folder
to the Manic Miners level folder, please let me know.

To run locally, run `yarn install` and `yarn start`, which will launch the
React application at `localhost:3000`. My dev machine is Linux so I have
no idea if this works on Windows.
React application at `localhost:3000`. I use Linux for my dev machine and have
no desire to test this setup on Windows, so your results may vary.

# Testing

Run Jest tests with `npx yarn test`. Some of the tests are "goldens" that
Run Jest tests with `yarn test`. Some of the tests are "goldens" that
test what a cavern serializes to. These can be automatically updated by
running `UPDATE_GOLDENS=1 npx yarn test`.
running `UPDATE_GOLDENS=1 yarn test`.

# Deploying

Expand All @@ -29,8 +29,8 @@ as long as all tests are passing.
# Contributing

I am on the Manic Miners Discord. Ping me if you're interested in contributing
and I can offer advice and/or suggestions. PRs are welcomed, but please run
`prettier` and ensure tests pass before submitting them.
and I can offer advice and/or suggestions. PRs are welcomed. Please run
`yarn presubmit` on your PR and commit any changes before submitting it.

# FAQ

Expand Down Expand Up @@ -106,4 +106,4 @@ this project hits a 1.0 release.

## Were any of these Qs actually FA'ed?

No.
[No.](https://www.youtube.com/watch?v=UUlcFEDkhsE)
88 changes: 66 additions & 22 deletions bin/architect-stats.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,34 @@
#!/usr/bin/env node
import 'dotenv/config';

// This script generates a large number of caverns until architects have been
// assigned, then reports statistics on how common the architects are.

import "dotenv/config";
import { inferContextDefaults } from "../src/core/common";
import { CAVERN_TF } from "../src/core/transformers";
import { AnyTfResultOf } from '../src/core/common/transform';
import { CollapseUnion } from '../src/core/common/utils';
import { MAX_PLUS_ONE } from '../src/core/common/prng';
import { ARCHITECTS } from '../src/core/architects';
import { AnyTfResultOf } from "../src/core/common/transform";
import { CollapseUnion } from "../src/core/common/utils";
import { MAX_PLUS_ONE } from "../src/core/common/prng";
import { ARCHITECTS } from "../src/core/architects";
import { Architect } from "../src/core/models/architect";

type Concordance = {[K: string]: number}
type Concordance = { [K: string]: number };

function gen(seed: number): Concordance {
let state = CAVERN_TF.first({
initialContext: inferContextDefaults({seed}),
initialContext: inferContextDefaults({ seed }),
});

function check(): Concordance | null {
const cavern: CollapseUnion<AnyTfResultOf<typeof CAVERN_TF>> = state.result;
if (!cavern.plans) {
return null;
}
const r: Concordance = {};
for (var i = 0; i < cavern.plans.length; i++) {
const plan: NonNullable<(typeof cavern)['plans']>[number] = cavern.plans[i];
if (!('architect' in plan && plan.architect)) {
const plan: NonNullable<(typeof cavern)["plans"]>[number] =
cavern.plans[i];
if (!("architect" in plan && plan.architect)) {
return null;
}
const name = plan.architect.name;
Expand All @@ -38,31 +44,69 @@ function gen(seed: number): Concordance {
return r;
}
}
throw new Error('Ended without architects somehow');
throw new Error("Ended without architects somehow");
}

function fgColor(a: Architect<any>) {
if (a.anchorBid) {
return 205;
} else if (a.scriptGlobals || a.script) {
return 45;
} else if (a.hallBid) {
return 228;
} else {
return 255;
}
}

type Stats = {[K: string]: number[]};
type Stats = { [K: string]: number[] };

function draw(stats: Stats, count: number) {
const width = 50;
const columnCount = 4;
const nameWidth = width - columnCount * 5;
const chartLines = ARCHITECTS.map(a => {
const cols = (stats[a.name] ?? []).filter((_, i) => i < columnCount).map(c => `${(c / count * 100).toFixed().padStart(3)}%`).join(' ');
return `${a.name.padEnd(nameWidth,'.').substring(0, nameWidth)} ${cols}`.padEnd(width);
const chartLines = ARCHITECTS.map((a, i) => {
const rowColor = `\x1b[38;5;${fgColor(a)};48;5;${i % 2 === 0 ? 232 : 234}m`;
const name = a.name.padEnd(nameWidth, ".").substring(0, nameWidth);
const cols = new Array(columnCount).fill(0)
.map((_, i) => (stats[a.name] ?? [])[i] ?? 0)
.map((c) => {
if (c <= 0) {
return ' ';
}
const ratio = c / count
const color = `\x1b[38;5;${Math.round(15 * ratio + 240)}m`;
if (ratio < 0.05) {
return `${color}${(ratio * 100).toFixed(1)}%`;
}
return `${color}${(ratio * 100).toFixed().padStart(3)}%`;
})
.join(" ");
return `${rowColor}${name} ${cols}\x1b[m`;
});

const mid = Math.ceil(chartLines.length / 2);
const header1 = `${'architect'.padEnd(nameWidth)} ${new Array(columnCount).fill(0).map((_, i) => ` >=${i + 1}`).join(' ')}`
const header2 = `${''.padEnd(nameWidth, '-')} ${new Array(columnCount).fill(0).map(() => '----').join(' ')}`
const chart = new Array(mid).fill(0).map((_, i) => `${chartLines[i]} | ${chartLines[i + mid] ?? ''}`).join('\n');
const footer = `Maps tested: ${count}`
const header1 = `${"architect".padEnd(nameWidth)} ${new Array(columnCount)
.fill(0)
.map((_, i) => ` >=${i + 1}`)
.join(" ")}`;
const header2 = `${"".padEnd(nameWidth, "-")} ${new Array(columnCount)
.fill(0)
.map(() => "----")
.join(" ")}`;
const chart = new Array(mid)
.fill(0)
.map((_, i) => `${chartLines[i]} | ${chartLines[i + mid] ?? ""}`)
.join("\n");
const footer = `Maps tested: ${count}`;
console.clear();
console.log(`${header1} | ${header1}\n${header2} + ${header2}\n${chart}\n${footer}`);
console.log(
`${header1} | ${header1}\n${header2} + ${header2}\n${chart}\n${footer}`,
);
}

function main() {
const firstSeed = 0xEFE63E54;
const firstSeed = 0xefe63e54;
const mapsToTest = 10000;
const stats: Stats = {};
for (var i = 0; i < mapsToTest; i++) {
Expand All @@ -81,4 +125,4 @@ function main() {
draw(stats, mapsToTest);
}

main();
main();
14 changes: 7 additions & 7 deletions bin/groundhog-gen.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
#!/usr/bin/env node
import 'dotenv/config';
import "dotenv/config";
import { inferContextDefaults } from "../src/core/common";
import { CAVERN_TF } from "../src/core/transformers";
import { SerializedCavern } from '../src/core/transformers/04_ephemera/05_serialize';
import { SerializedCavern } from "../src/core/transformers/04_ephemera/05_serialize";

function generate() {
const seed = parseInt(process.env.SEED ?? '0', 16);
const seed = parseInt(process.env.SEED ?? "0", 16);
if (!seed) {
throw new Error('Usage:\n SEED=... yarn gen');
throw new Error("Usage:\n SEED=... yarn gen");
}
let state = CAVERN_TF.first({
initialContext: inferContextDefaults({seed}),
initialContext: inferContextDefaults({ seed }),
});

while (state.next) {
state = state.next();
}

console.log((state.result as SerializedCavern).serialized);
}

generate();
generate();
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "groundhog",
"version": "0.10.3",
"version": "0.10.4",
"homepage": "https://charredutensil.github.io/groundhog",
"private": true,
"dependencies": {
Expand All @@ -23,6 +23,7 @@
"test": "react-scripts test",
"predeploy": "react-scripts build",
"deploy": "gh-pages -d build",
"presubmit": "prettier --write src && eslint --fix src --max-warnings=0 && react-scripts test --all",
"eject": "react-scripts eject",
"gen": "tsx bin/groundhog-gen",
"architect-stats": "tsx bin/architect-stats"
Expand All @@ -48,6 +49,7 @@
"devDependencies": {
"@types/delaunator": "^5.0.2",
"dotenv": "^16.4.5",
"prettier": "^3.3.3",
"tsx": "^4.19.1"
}
}
4 changes: 0 additions & 4 deletions src/core/architects/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,5 @@ export const [DefaultCaveArchitect, DefaultHallArchitect] = (
objectives: () => undefined,
maxSlope: undefined,
claimEventOnDiscover: () => [],
scriptGlobals: () => undefined,
script: () => undefined,
monsterSpawnScript: () => undefined,
slugSpawnScript: () => undefined,
}) as PartialArchitect<any>,
);
1 change: 0 additions & 1 deletion src/core/lore/graphs/completeness.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
FOUND_ALL_LOST_MINERS,
FOUND_HOARD,
FOUND_HQ,
FOUND_LOST_MINERS,
FOUND_SLUG_NEST,
} from "./events";
import { NAME } from "./names";
Expand Down
8 changes: 4 additions & 4 deletions src/core/models/architect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,16 +143,16 @@ export type BaseArchitect<T extends BaseMetadata> = {
cavern: EnscribedCavern;
plan: Plan<T>;
}): { priority: number; dz?: DiscoveryZone; pos?: Point }[];
scriptGlobals(args: { cavern: PreprogrammedCavern }): string | undefined;
script(args: {
scriptGlobals?(args: { cavern: PreprogrammedCavern }): string | undefined;
script?(args: {
cavern: PreprogrammedCavern;
plan: Plan<T>;
}): string | undefined;
monsterSpawnScript(args: {
monsterSpawnScript?(args: {
cavern: PreprogrammedCavern;
plan: Plan<T>;
}): string | undefined;
slugSpawnScript(args: {
slugSpawnScript?(args: {
cavern: PreprogrammedCavern;
plan: Plan<T>;
}): string | undefined;
Expand Down
23 changes: 13 additions & 10 deletions src/core/transformers/04_ephemera/04_program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,32 @@ export type ProgrammedCavern = PreprogrammedCavern & {
export default function program(cavern: PreprogrammedCavern): ProgrammedCavern {
// All unique globals function objects
const globalsFns = Array.from(
cavern.plans.reduce((r: Architect<any>["scriptGlobals"][], plan) => {
const fn = plan.architect.scriptGlobals;
if (!r.some((f) => Object.is(fn, f))) {
r.push(fn);
}
return r;
}, []),
cavern.plans.reduce(
(r: NonNullable<Architect<any>["scriptGlobals"]>[], plan) => {
const fn = plan.architect.scriptGlobals;
if (fn && !r.some((f) => Object.is(fn, f))) {
r.push(fn);
}
return r;
},
[],
),
);
const archGlobals = filterTruthy(globalsFns.map((fn) => fn({ cavern })));
const archScripts = filterTruthy(
cavern.plans.map((plan) => plan.architect.script({ cavern, plan })),
cavern.plans.map((plan) => plan.architect.script?.({ cavern, plan })),
);
const monsters = cavern.context.hasMonsters
? filterTruthy(
cavern.plans.map((plan) =>
plan.architect.monsterSpawnScript({ cavern, plan }),
plan.architect.monsterSpawnScript?.({ cavern, plan }),
),
)
: [];
const slugs = cavern.context.hasSlugs
? filterTruthy(
cavern.plans.map((plan) =>
plan.architect.slugSpawnScript({ cavern, plan }),
plan.architect.slugSpawnScript?.({ cavern, plan }),
),
)
: [];
Expand Down
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -9599,6 +9599,11 @@ prelude-ls@~1.1.2:
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==

prettier@^3.3.3:
version "3.3.3"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.3.3.tgz#30c54fe0be0d8d12e6ae61dbb10109ea00d53105"
integrity sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==

pretty-bytes@^5.3.0, pretty-bytes@^5.4.1:
version "5.6.0"
resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb"
Expand Down

0 comments on commit 899f875

Please sign in to comment.