Skip to content

Commit 1c5917c

Browse files
feat: Operators
1 parent e2de2fb commit 1c5917c

37 files changed

+612
-102
lines changed

packages/kitten-analysts/examples/a708b0ad-5f94-4466-8a2e-1d381117d0e0.json

+1-1
Large diffs are not rendered by default.

packages/kitten-analysts/examples/ka-internal-savestate.json

+1-1
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { Operator } from "./GraphSolver.js";
2+
3+
export class GraphDotPrinter {
4+
print(
5+
node: Operator,
6+
seen = new Set<Operator>(),
7+
indent = 0,
8+
dotBuffer = new Array<string>(),
9+
): Array<string> {
10+
if (30 < indent) {
11+
throw new Error("graph too deep");
12+
}
13+
14+
if (seen.has(node)) {
15+
console.info(`${" ".repeat(indent)}${node.name} ↻ loops back into graph`);
16+
return dotBuffer;
17+
}
18+
19+
seen.add(node);
20+
21+
if (0 < node.requires.length && node.children.size === 0) {
22+
console.warn(`${" ".repeat(indent)}${node.name} 🗲 unsolved!`);
23+
return dotBuffer;
24+
}
25+
26+
console.info(`${" ".repeat(indent)}${node.name}`);
27+
dotBuffer.push(
28+
`"${node.name}"${indent === 0 ? " [shape=circle, color=darkolivegreen3, style=filled]" : ""}`,
29+
);
30+
31+
if (node.requires.length === 0) {
32+
return dotBuffer;
33+
}
34+
35+
const requirements = new Set(node.requires);
36+
for (const child of node.children) {
37+
this.print(child, seen, indent + 1, dotBuffer);
38+
dotBuffer.push(`"${child.name}" -> "${node.name}"`);
39+
for (const solution of child.solves) {
40+
requirements.delete(solution);
41+
}
42+
}
43+
if (0 < requirements.size) {
44+
console.warn(
45+
`${" ".repeat(indent)}🗲 parent was left partially unsolved: ${[...requirements.values()].join(", ")}`,
46+
);
47+
}
48+
49+
return dotBuffer;
50+
}
51+
}

packages/kitten-engineers/source/GraphPrinter.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export class GraphPrinter {
3333
}
3434
if (0 < requirements.size) {
3535
console.warn(
36-
`${" ".repeat(indent)}🗲 parent was left partially unsolved: ${[...requirements].join(", ")}`,
36+
`${" ".repeat(indent)}🗲 parent was left partially unsolved: ${[...requirements.values()].join(", ")}`,
3737
);
3838
}
3939
}

packages/kitten-engineers/source/GraphSolver.ts

+21-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,24 @@
11
import { PayloadBuildings } from "@kitten-science/kitten-analysts/KittenAnalysts.js";
22
import { EngineState } from "@kitten-science/kitten-scientists/Engine.js";
33
import { Game } from "@kitten-science/kitten-scientists/types/game.js";
4-
import { Buildings, Resources } from "@kitten-science/kitten-scientists/types/index.js";
4+
import {
5+
Buildings,
6+
ReligionUpgrades,
7+
Resources,
8+
StagedBuildings,
9+
Technologies,
10+
Upgrades,
11+
} from "@kitten-science/kitten-scientists/types/index.js";
512
import { TreeNode } from "@oliversalzburg/js-utils/data/tree.js";
613

7-
export const Solutions = [...Buildings, ...Resources] as const;
14+
export const Solutions = [
15+
...Buildings,
16+
...StagedBuildings,
17+
...ReligionUpgrades,
18+
...Resources,
19+
...Technologies,
20+
...Upgrades,
21+
] as const;
822
export type Solution = (typeof Solutions)[number];
923

1024
export interface Operator extends TreeNode<Operator> {
@@ -31,6 +45,11 @@ export class GraphSolver {
3145

3246
solve(node: Operator, root: Operator = node, parents: Iterable<Operator> = []): Operator {
3347
for (const operator of this.operators) {
48+
// We might want to allow some operators to solve themselves,
49+
// but it seems counter-productive for the time being.
50+
if (operator === node) {
51+
continue;
52+
}
3453
if (!operator.solves.some(solution => node.requires.includes(solution))) {
3554
continue;
3655
}

packages/kitten-engineers/source/KittenEngineers.ts

+75-36
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,39 @@
11
import "@kitten-science/kitten-analysts/KittenAnalysts.js";
2-
import { Game, I18nEngine, Resources } from "@kitten-science/kitten-scientists/types/index.js";
2+
import {
3+
Buildings,
4+
Game,
5+
I18nEngine,
6+
Resources,
7+
Technologies,
8+
Upgrades,
9+
} from "@kitten-science/kitten-scientists/types/index.js";
310
import { isNil } from "@oliversalzburg/js-utils/data/nil.js";
4-
import { AssignMiner } from "./examples/assign-miner-operator.js";
5-
import { AssignWoodcutter } from "./examples/assign-woodcutter-operator.js";
6-
import { BuildCatnipField } from "./examples/build-catnip-field-operator.js";
7-
import { BuildHut } from "./examples/build-hut-operator.js";
8-
import { BuildLogHouse } from "./examples/build-log-house-operator.js";
9-
import { ConsumeStockResourceFactory } from "./examples/consume-stock-resource.js";
10-
import { GatherCatnip } from "./examples/gather-catnip-operator.js";
11-
import { RefineCatnip } from "./examples/refine-catnip-operator.js";
12-
import { TradeLizards } from "./examples/trade-lizards-operator.js";
13-
import { TradeNagas } from "./examples/trade-nagas-operator.js";
14-
import { GraphPrinter } from "./GraphPrinter.js";
11+
import { GraphDotPrinter } from "./GraphDotPrinter.js";
1512
import { GraphSolver, Operator } from "./GraphSolver.js";
13+
import { AssignFarmer } from "./operators/assign-farmer.js";
14+
import { AssignGeologist } from "./operators/assign-geologist.js";
15+
import { AssignHunter } from "./operators/assign-hunter.js";
16+
import { AssignMiner } from "./operators/assign-miner.js";
17+
import { AssignPriest } from "./operators/assign-priest.js";
18+
import { AssignScholar } from "./operators/assign-scholar.js";
19+
import { AssignWoodcutter } from "./operators/assign-woodcutter.js";
20+
import { BuildBonfireFactory } from "./operators/build-bonfire.js";
21+
import { ConsumeStockResourceFactory } from "./operators/consume-stock-resource.js";
22+
import { CraftAlloy } from "./operators/craft-alloy.js";
23+
import { CraftBlueprint } from "./operators/craft-blueprint.js";
24+
import { CraftCompedium } from "./operators/craft-compedium.js";
25+
import { CraftGear } from "./operators/craft-gear.js";
26+
import { CraftManuscript } from "./operators/craft-manuscript.js";
27+
import { CraftParchment } from "./operators/craft-parchment.js";
28+
import { CraftPlate } from "./operators/craft-plate.js";
29+
import { GatherCatnip } from "./operators/gather-catnip.js";
30+
import { Hunt } from "./operators/hunt.js";
31+
import { RefineCatnip } from "./operators/refine-catnip.js";
32+
import { TradeLizards } from "./operators/trade-lizards.js";
33+
import { TradeNagas } from "./operators/trade-nagas.js";
34+
import { UnlockSolarRevolution } from "./operators/unlock-solar-revolution.js";
35+
import { UnlockTechnologyFactory } from "./operators/unlock-technology.js";
36+
import { UnlockUpgradeFactory } from "./operators/unlock-upgrade.js";
1637
import { cinfo } from "./tools/Log.js";
1738

1839
declare global {
@@ -58,6 +79,48 @@ export class KittenEngineers {
5879
this.#interval = window.setInterval(() => {
5980
this.snapshot();
6081
}, 5000);
82+
83+
// Build the list of available operators.
84+
const root = new UnlockSolarRevolution();
85+
const operators: Array<Operator> = [
86+
new AssignFarmer(),
87+
new AssignGeologist(),
88+
new AssignHunter(),
89+
new AssignMiner(),
90+
new AssignPriest(),
91+
new AssignScholar(),
92+
new AssignWoodcutter(),
93+
new CraftAlloy(),
94+
new CraftBlueprint(),
95+
new CraftCompedium(),
96+
new CraftGear(),
97+
new CraftManuscript(),
98+
new CraftParchment(),
99+
new CraftPlate(),
100+
new GatherCatnip(),
101+
new Hunt(),
102+
new RefineCatnip(),
103+
new TradeLizards(),
104+
new TradeNagas(),
105+
];
106+
107+
for (const OperatorConstructor of BuildBonfireFactory(Buildings)) {
108+
operators.push(new OperatorConstructor());
109+
}
110+
for (const OperatorConstructor of ConsumeStockResourceFactory(Resources)) {
111+
operators.push(new OperatorConstructor());
112+
}
113+
for (const OperatorConstructor of UnlockTechnologyFactory(Technologies)) {
114+
operators.push(new OperatorConstructor());
115+
}
116+
for (const OperatorConstructor of UnlockUpgradeFactory(Upgrades)) {
117+
operators.push(new OperatorConstructor());
118+
}
119+
120+
const solver = new GraphSolver(operators);
121+
const graph = solver.solve(root);
122+
const dotGraph = new GraphDotPrinter().print(graph);
123+
console.log(dotGraph.join("\n"));
61124
}
62125

63126
stop() {
@@ -76,30 +139,6 @@ export class KittenEngineers {
76139
if (isNil(window.kittenScientists) || isNil(window.kittenAnalysts)) {
77140
return;
78141
}
79-
80-
return;
81-
82-
// Build the list of available operators.
83-
const root = new BuildHut();
84-
const operators: Array<Operator> = [
85-
root,
86-
new AssignMiner(),
87-
new AssignWoodcutter(),
88-
new BuildCatnipField(),
89-
new BuildLogHouse(),
90-
new GatherCatnip(),
91-
new RefineCatnip(),
92-
new TradeLizards(),
93-
new TradeNagas(),
94-
];
95-
96-
for (const OperatorConstructor of ConsumeStockResourceFactory(Resources)) {
97-
operators.push(new OperatorConstructor());
98-
}
99-
100-
const solver = new GraphSolver(operators);
101-
const graph = solver.solve(root);
102-
new GraphPrinter().print(graph);
103142
};
104143
savegameHandler = () => {};
105144

packages/kitten-engineers/source/RootSolver.ts

-8
This file was deleted.

packages/kitten-engineers/source/examples/build-catnip-field-operator.ts packages/kitten-engineers/source/operators/assign-farmer.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import { Game } from "@kitten-science/kitten-scientists/types/game.js";
44
import { TreeNode } from "@oliversalzburg/js-utils/data/tree.js";
55
import { Operator } from "../GraphSolver.js";
66

7-
export class BuildCatnipField extends TreeNode<Operator> implements Operator {
8-
name = "build catnip field";
7+
export class AssignFarmer extends TreeNode<Operator> implements Operator {
8+
name = "assign farmer";
99

10-
requires = ["catnip" as const];
10+
requires = ["kittens" as const, "agriculture" as const];
1111
solves = ["catnip" as const];
1212

1313
ancestors = new Set<Operator>();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { PayloadBuildings } from "@kitten-science/kitten-analysts/KittenAnalysts.js";
2+
import { EngineState } from "@kitten-science/kitten-scientists/Engine.js";
3+
import { Game } from "@kitten-science/kitten-scientists/types/game.js";
4+
import { TreeNode } from "@oliversalzburg/js-utils/data/tree.js";
5+
import { Operator } from "../GraphSolver.js";
6+
import { cdebug } from "../tools/Log.js";
7+
8+
export class AssignGeologist extends TreeNode<Operator> implements Operator {
9+
name = "assign geologist";
10+
11+
requires = ["kittens" as const, "archeology" as const, "geodesy" as const];
12+
solves = ["coal" as const, "gold" as const];
13+
14+
ancestors = new Set<Operator>();
15+
16+
calculateCost() {
17+
return 0;
18+
}
19+
20+
execute(_game: Game, state: EngineState, snapshots: { buildings: PayloadBuildings }) {
21+
cdebug(
22+
"Solar Revolution is currently at value:",
23+
snapshots.buildings.find(b => b.name === "solarRevolution")?.value,
24+
);
25+
return state;
26+
}
27+
}

packages/kitten-engineers/source/examples/build-log-house-operator.ts packages/kitten-engineers/source/operators/assign-hunter.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ import { PayloadBuildings } from "@kitten-science/kitten-analysts/KittenAnalysts
22
import { EngineState } from "@kitten-science/kitten-scientists/Engine.js";
33
import { Game } from "@kitten-science/kitten-scientists/types/game.js";
44
import { TreeNode } from "@oliversalzburg/js-utils/data/tree.js";
5-
import { Operator, Solution } from "../GraphSolver.js";
5+
import { Operator } from "../GraphSolver.js";
66

7-
export class BuildLogHouse extends TreeNode<Operator> implements Operator {
8-
name = "build log house";
7+
export class AssignHunter extends TreeNode<Operator> implements Operator {
8+
name = "assign hunter";
99

10-
requires: Array<Solution> = ["minerals", "wood"];
11-
solves: Array<Solution> = ["kittens"];
10+
requires = ["kittens" as const];
11+
solves = ["manpower" as const];
1212

1313
ancestors = new Set<Operator>();
1414

packages/kitten-engineers/source/examples/assign-miner-operator.ts packages/kitten-engineers/source/operators/assign-miner.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { cdebug } from "../tools/Log.js";
88
export class AssignMiner extends TreeNode<Operator> implements Operator {
99
name = "assign miner";
1010

11-
requires = ["kittens" as const];
11+
requires = ["kittens" as const, "mine" as const];
1212
solves = ["minerals" as const];
1313

1414
ancestors = new Set<Operator>();

packages/kitten-engineers/source/examples/build-hut-operator.ts packages/kitten-engineers/source/operators/assign-priest.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ import { TreeNode } from "@oliversalzburg/js-utils/data/tree.js";
55
import { Operator } from "../GraphSolver.js";
66
import { cdebug } from "../tools/Log.js";
77

8-
export class BuildHut extends TreeNode<Operator> implements Operator {
9-
name = "build hut";
8+
export class AssignPriest extends TreeNode<Operator> implements Operator {
9+
name = "assign priest";
1010

11-
requires = ["wood" as const];
12-
solves = ["kittens" as const];
11+
requires = ["kittens" as const, "theology" as const];
12+
solves = ["faith" as const];
1313

1414
ancestors = new Set<Operator>();
1515

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { PayloadBuildings } from "@kitten-science/kitten-analysts/KittenAnalysts.js";
2+
import { EngineState } from "@kitten-science/kitten-scientists/Engine.js";
3+
import { Game } from "@kitten-science/kitten-scientists/types/game.js";
4+
import { TreeNode } from "@oliversalzburg/js-utils/data/tree.js";
5+
import { Operator } from "../GraphSolver.js";
6+
7+
export class AssignScholar extends TreeNode<Operator> implements Operator {
8+
name = "assign scholar";
9+
10+
requires = ["kittens" as const, "library" as const, "astrophysicists" as const];
11+
solves = ["science" as const, "starchart" as const];
12+
13+
ancestors = new Set<Operator>();
14+
15+
calculateCost() {
16+
return 0;
17+
}
18+
19+
execute(_game: Game, state: EngineState, _snapshots: { buildings: PayloadBuildings }) {
20+
return state;
21+
}
22+
}

0 commit comments

Comments
 (0)