Skip to content

Commit

Permalink
feat: Operators
Browse files Browse the repository at this point in the history
  • Loading branch information
oliversalzburg committed Sep 7, 2024
1 parent e2de2fb commit 1c5917c
Show file tree
Hide file tree
Showing 37 changed files with 612 additions and 102 deletions.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

51 changes: 51 additions & 0 deletions packages/kitten-engineers/source/GraphDotPrinter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Operator } from "./GraphSolver.js";

export class GraphDotPrinter {
print(
node: Operator,
seen = new Set<Operator>(),
indent = 0,
dotBuffer = new Array<string>(),
): Array<string> {
if (30 < indent) {
throw new Error("graph too deep");
}

if (seen.has(node)) {
console.info(`${" ".repeat(indent)}${node.name} ↻ loops back into graph`);
return dotBuffer;
}

seen.add(node);

if (0 < node.requires.length && node.children.size === 0) {
console.warn(`${" ".repeat(indent)}${node.name} 🗲 unsolved!`);
return dotBuffer;
}

console.info(`${" ".repeat(indent)}${node.name}`);
dotBuffer.push(
`"${node.name}"${indent === 0 ? " [shape=circle, color=darkolivegreen3, style=filled]" : ""}`,
);

if (node.requires.length === 0) {
return dotBuffer;
}

const requirements = new Set(node.requires);
for (const child of node.children) {
this.print(child, seen, indent + 1, dotBuffer);
dotBuffer.push(`"${child.name}" -> "${node.name}"`);
for (const solution of child.solves) {
requirements.delete(solution);
}
}
if (0 < requirements.size) {
console.warn(
`${" ".repeat(indent)}🗲 parent was left partially unsolved: ${[...requirements.values()].join(", ")}`,
);
}

return dotBuffer;
}
}
2 changes: 1 addition & 1 deletion packages/kitten-engineers/source/GraphPrinter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export class GraphPrinter {
}
if (0 < requirements.size) {
console.warn(
`${" ".repeat(indent)}🗲 parent was left partially unsolved: ${[...requirements].join(", ")}`,
`${" ".repeat(indent)}🗲 parent was left partially unsolved: ${[...requirements.values()].join(", ")}`,
);
}
}
Expand Down
23 changes: 21 additions & 2 deletions packages/kitten-engineers/source/GraphSolver.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
import { PayloadBuildings } from "@kitten-science/kitten-analysts/KittenAnalysts.js";
import { EngineState } from "@kitten-science/kitten-scientists/Engine.js";
import { Game } from "@kitten-science/kitten-scientists/types/game.js";
import { Buildings, Resources } from "@kitten-science/kitten-scientists/types/index.js";
import {
Buildings,
ReligionUpgrades,
Resources,
StagedBuildings,
Technologies,
Upgrades,
} from "@kitten-science/kitten-scientists/types/index.js";
import { TreeNode } from "@oliversalzburg/js-utils/data/tree.js";

export const Solutions = [...Buildings, ...Resources] as const;
export const Solutions = [
...Buildings,
...StagedBuildings,
...ReligionUpgrades,
...Resources,
...Technologies,
...Upgrades,
] as const;
export type Solution = (typeof Solutions)[number];

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

solve(node: Operator, root: Operator = node, parents: Iterable<Operator> = []): Operator {
for (const operator of this.operators) {
// We might want to allow some operators to solve themselves,
// but it seems counter-productive for the time being.
if (operator === node) {
continue;
}
if (!operator.solves.some(solution => node.requires.includes(solution))) {
continue;
}
Expand Down
111 changes: 75 additions & 36 deletions packages/kitten-engineers/source/KittenEngineers.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,39 @@
import "@kitten-science/kitten-analysts/KittenAnalysts.js";
import { Game, I18nEngine, Resources } from "@kitten-science/kitten-scientists/types/index.js";
import {
Buildings,
Game,
I18nEngine,
Resources,
Technologies,
Upgrades,
} from "@kitten-science/kitten-scientists/types/index.js";
import { isNil } from "@oliversalzburg/js-utils/data/nil.js";
import { AssignMiner } from "./examples/assign-miner-operator.js";
import { AssignWoodcutter } from "./examples/assign-woodcutter-operator.js";
import { BuildCatnipField } from "./examples/build-catnip-field-operator.js";
import { BuildHut } from "./examples/build-hut-operator.js";
import { BuildLogHouse } from "./examples/build-log-house-operator.js";
import { ConsumeStockResourceFactory } from "./examples/consume-stock-resource.js";
import { GatherCatnip } from "./examples/gather-catnip-operator.js";
import { RefineCatnip } from "./examples/refine-catnip-operator.js";
import { TradeLizards } from "./examples/trade-lizards-operator.js";
import { TradeNagas } from "./examples/trade-nagas-operator.js";
import { GraphPrinter } from "./GraphPrinter.js";
import { GraphDotPrinter } from "./GraphDotPrinter.js";
import { GraphSolver, Operator } from "./GraphSolver.js";
import { AssignFarmer } from "./operators/assign-farmer.js";
import { AssignGeologist } from "./operators/assign-geologist.js";
import { AssignHunter } from "./operators/assign-hunter.js";
import { AssignMiner } from "./operators/assign-miner.js";
import { AssignPriest } from "./operators/assign-priest.js";
import { AssignScholar } from "./operators/assign-scholar.js";
import { AssignWoodcutter } from "./operators/assign-woodcutter.js";
import { BuildBonfireFactory } from "./operators/build-bonfire.js";
import { ConsumeStockResourceFactory } from "./operators/consume-stock-resource.js";
import { CraftAlloy } from "./operators/craft-alloy.js";
import { CraftBlueprint } from "./operators/craft-blueprint.js";
import { CraftCompedium } from "./operators/craft-compedium.js";
import { CraftGear } from "./operators/craft-gear.js";
import { CraftManuscript } from "./operators/craft-manuscript.js";
import { CraftParchment } from "./operators/craft-parchment.js";
import { CraftPlate } from "./operators/craft-plate.js";
import { GatherCatnip } from "./operators/gather-catnip.js";
import { Hunt } from "./operators/hunt.js";
import { RefineCatnip } from "./operators/refine-catnip.js";
import { TradeLizards } from "./operators/trade-lizards.js";
import { TradeNagas } from "./operators/trade-nagas.js";
import { UnlockSolarRevolution } from "./operators/unlock-solar-revolution.js";
import { UnlockTechnologyFactory } from "./operators/unlock-technology.js";
import { UnlockUpgradeFactory } from "./operators/unlock-upgrade.js";
import { cinfo } from "./tools/Log.js";

declare global {
Expand Down Expand Up @@ -58,6 +79,48 @@ export class KittenEngineers {
this.#interval = window.setInterval(() => {
this.snapshot();
}, 5000);

// Build the list of available operators.
const root = new UnlockSolarRevolution();
const operators: Array<Operator> = [
new AssignFarmer(),
new AssignGeologist(),
new AssignHunter(),
new AssignMiner(),
new AssignPriest(),
new AssignScholar(),
new AssignWoodcutter(),
new CraftAlloy(),
new CraftBlueprint(),
new CraftCompedium(),
new CraftGear(),
new CraftManuscript(),
new CraftParchment(),
new CraftPlate(),
new GatherCatnip(),
new Hunt(),
new RefineCatnip(),
new TradeLizards(),
new TradeNagas(),
];

for (const OperatorConstructor of BuildBonfireFactory(Buildings)) {
operators.push(new OperatorConstructor());
}
for (const OperatorConstructor of ConsumeStockResourceFactory(Resources)) {
operators.push(new OperatorConstructor());
}
for (const OperatorConstructor of UnlockTechnologyFactory(Technologies)) {
operators.push(new OperatorConstructor());
}
for (const OperatorConstructor of UnlockUpgradeFactory(Upgrades)) {
operators.push(new OperatorConstructor());
}

const solver = new GraphSolver(operators);
const graph = solver.solve(root);
const dotGraph = new GraphDotPrinter().print(graph);
console.log(dotGraph.join("\n"));
}

stop() {
Expand All @@ -76,30 +139,6 @@ export class KittenEngineers {
if (isNil(window.kittenScientists) || isNil(window.kittenAnalysts)) {
return;
}

return;

// Build the list of available operators.
const root = new BuildHut();
const operators: Array<Operator> = [
root,
new AssignMiner(),
new AssignWoodcutter(),
new BuildCatnipField(),
new BuildLogHouse(),
new GatherCatnip(),
new RefineCatnip(),
new TradeLizards(),
new TradeNagas(),
];

for (const OperatorConstructor of ConsumeStockResourceFactory(Resources)) {
operators.push(new OperatorConstructor());
}

const solver = new GraphSolver(operators);
const graph = solver.solve(root);
new GraphPrinter().print(graph);
};
savegameHandler = () => {};

Expand Down
8 changes: 0 additions & 8 deletions packages/kitten-engineers/source/RootSolver.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import { Game } from "@kitten-science/kitten-scientists/types/game.js";
import { TreeNode } from "@oliversalzburg/js-utils/data/tree.js";
import { Operator } from "../GraphSolver.js";

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

requires = ["catnip" as const];
requires = ["kittens" as const, "agriculture" as const];
solves = ["catnip" as const];

ancestors = new Set<Operator>();
Expand Down
27 changes: 27 additions & 0 deletions packages/kitten-engineers/source/operators/assign-geologist.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { PayloadBuildings } from "@kitten-science/kitten-analysts/KittenAnalysts.js";
import { EngineState } from "@kitten-science/kitten-scientists/Engine.js";
import { Game } from "@kitten-science/kitten-scientists/types/game.js";
import { TreeNode } from "@oliversalzburg/js-utils/data/tree.js";
import { Operator } from "../GraphSolver.js";
import { cdebug } from "../tools/Log.js";

export class AssignGeologist extends TreeNode<Operator> implements Operator {
name = "assign geologist";

requires = ["kittens" as const, "archeology" as const, "geodesy" as const];
solves = ["coal" as const, "gold" as const];

ancestors = new Set<Operator>();

calculateCost() {
return 0;
}

execute(_game: Game, state: EngineState, snapshots: { buildings: PayloadBuildings }) {
cdebug(
"Solar Revolution is currently at value:",
snapshots.buildings.find(b => b.name === "solarRevolution")?.value,
);
return state;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import { PayloadBuildings } from "@kitten-science/kitten-analysts/KittenAnalysts
import { EngineState } from "@kitten-science/kitten-scientists/Engine.js";
import { Game } from "@kitten-science/kitten-scientists/types/game.js";
import { TreeNode } from "@oliversalzburg/js-utils/data/tree.js";
import { Operator, Solution } from "../GraphSolver.js";
import { Operator } from "../GraphSolver.js";

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

requires: Array<Solution> = ["minerals", "wood"];
solves: Array<Solution> = ["kittens"];
requires = ["kittens" as const];
solves = ["manpower" as const];

ancestors = new Set<Operator>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { cdebug } from "../tools/Log.js";
export class AssignMiner extends TreeNode<Operator> implements Operator {
name = "assign miner";

requires = ["kittens" as const];
requires = ["kittens" as const, "mine" as const];
solves = ["minerals" as const];

ancestors = new Set<Operator>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import { TreeNode } from "@oliversalzburg/js-utils/data/tree.js";
import { Operator } from "../GraphSolver.js";
import { cdebug } from "../tools/Log.js";

export class BuildHut extends TreeNode<Operator> implements Operator {
name = "build hut";
export class AssignPriest extends TreeNode<Operator> implements Operator {
name = "assign priest";

requires = ["wood" as const];
solves = ["kittens" as const];
requires = ["kittens" as const, "theology" as const];
solves = ["faith" as const];

ancestors = new Set<Operator>();

Expand Down
22 changes: 22 additions & 0 deletions packages/kitten-engineers/source/operators/assign-scholar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { PayloadBuildings } from "@kitten-science/kitten-analysts/KittenAnalysts.js";
import { EngineState } from "@kitten-science/kitten-scientists/Engine.js";
import { Game } from "@kitten-science/kitten-scientists/types/game.js";
import { TreeNode } from "@oliversalzburg/js-utils/data/tree.js";
import { Operator } from "../GraphSolver.js";

export class AssignScholar extends TreeNode<Operator> implements Operator {
name = "assign scholar";

requires = ["kittens" as const, "library" as const, "astrophysicists" as const];
solves = ["science" as const, "starchart" as const];

ancestors = new Set<Operator>();

calculateCost() {
return 0;
}

execute(_game: Game, state: EngineState, _snapshots: { buildings: PayloadBuildings }) {
return state;
}
}
Loading

0 comments on commit 1c5917c

Please sign in to comment.