parametric game fules. minor simulator refactoring

This commit is contained in:
azykov@mail.ru
2026-02-19 22:19:05 +03:00
parent ef5caab748
commit d07f2043e7
22 changed files with 1170 additions and 181 deletions
+3
View File
@@ -14,7 +14,10 @@ export class Game {
rules: GameRules,
) {
this.rules = rules;
this.reset();
}
public reset() {
this.initResources();
this.initGenerators();
}
+1 -1
View File
@@ -112,7 +112,7 @@ export class ResourceGenerator {
// }
public getGain(level?: number): ResourceSet {
return this.rule.generationGain(level ?? this.level, this.game);
return this.rule.generationGain(level ?? this.level, this.game).mul(this.getPeriod(level));
}
public snapshot(): ResourceGeneratorSnapshot {
+7 -1
View File
@@ -79,8 +79,14 @@ export class ResourceSet {
return inst;
}
public div(factor: number): ResourceSet {
const inst = new ResourceSet();
ResourceSet.transform(this.keys(), inst, (res) => this.get(res).divide(factor));
return inst;
}
public toString(): string {
return Array.from(this.items).map(([k, v]) => `${v} ${k}`).join(', ');
return Array.from(this.items).map(([k, v]) => `${formatNumber(v)} ${k}`).join(', ');
}
public toObject(): PartialResources {
+3 -3
View File
@@ -5,8 +5,8 @@ export * from './rules';
export * from './game';
export * from './actions';
import { rules } from './rulesets/rules';
import { rules, type GameRulesOptions } from './rulesets/rules';
export function makeGame(): Game {
return new Game(rules)
export function makeGame(options: GameRulesOptions): Game {
return new Game(rules(options))
};
+65
View File
@@ -0,0 +1,65 @@
import Decimal from "break_eternity.js";
import type { GameRules } from "../rules/GameRules";
import { simpleResGen, resourceGen } from './utils';
import { ResourceSet } from "../game";
export const rules: GameRules = {
startResources: { prestigeMul: new Decimal(1) },
generators: [
{
name: 'white',
startingLevel: 1,
resetProgressOnUpgrade: true,
manualGeneration: true,
period: (level) => level ? 3 : 0,
isVisible: () => true,
upgradePrice: () => ResourceSet.zero,
isUpgradable: () => false,
generationGain: (level, game) => resourceGen({ RED: { offset: 14, power: 2, factor: 1 } }, level),
onGenerate: (level, game, gain) => game.resources.add(gain),
},
// simpleResGen({
// name: 'red',
// startingLevel: 1,
// period: (level) => level === 0 ? 0 : Math.pow(0.95, level - 1),
// upgradePrice: { RED: { offset: 3, power: 2.1, factor: 10 } },
// generatesResources: { RED: { offset: 3, power: 1.5, factor: 1 } },
// }),
// ...['red', 'orange', 'yellow', 'green', 'cyan', 'blue', 'violet']
...['red', 'orange', 'yellow']
.map((color, idx) => simpleResGen({
name: color,
startingLevel: idx === 0 ? 1 : 0,
period: (level) => level === 0 ? 0 : Math.pow(0.95, level) + 0.05,
generatesResources: { RED: { offset: 2 * 15 ** idx, power: 1, factor: 1 * 15 ** idx } },
// visibilityPrice: { RED: { offset: 5 * 10 ** (idx + 1) } },
upgradePrice: { RED: { offset: 2 * 20 ** idx, power: 2.6, factor: 3 * 15 ** idx } }
})),
// {
// name: 'prestige',
// startingLevel: 0,
// period: () => 0,
// // generation: resourceGen({ prestigeMul: { offset: 0, power: 0.2, factor: 1 } }),
// openPrice: (level) => ({ RED: new Decimal(50) }),
// upgradePrice: () => ({}),
// visibilityPrice: () => ({}),
// manualProgressReset: true,
// resetProgressOnUpgrade: true,
// generation: (level, game, dryRun) => {
// if (!dryRun) {
// console.log('prestige ' + level);
// game.generators.filter((gen) => gen.rule.name !== 'prestige').forEach((gen) => gen.reset());
// ResourceSet.transform(
// game.resources.keys().filter((res) => res != 'prestigeMul'),
// game.resources,
// () => Decimal.dZero,
// );
// }
// return resourceGen({ prestigeMul: { offset: 0, power: 1, factor: 1 } })(level, game);
// // game.resources.clear();
// }
// }
],
}
+44 -29
View File
@@ -1,37 +1,52 @@
import Decimal from "break_eternity.js";
import type { GameRules } from "../rules/GameRules";
import { simpleResGen, resourceGen } from './utils';
import { resourceGen, classicResGen } from './utils';
import { ResourceSet } from "../game";
export const rules: GameRules = {
startResources: { prestigeMul: new Decimal(1) },
export type GameRulesOptions = {
startingResources: number,
upgradeBasePrice: number,
generationBasePrice: number,
upgradeFactor: number,
generationPower: number,
}
export const defaultGameRulesOptions: GameRulesOptions = {
startingResources: 100,
generationBasePrice: 0.2,
generationPower: 0.3,
upgradeBasePrice: 100,
upgradeFactor: 1.01,
}
export const rules: (options: GameRulesOptions) => GameRules = (options) => ({
startResources: { RED: new Decimal(options.startingResources), prestigeMul: new Decimal(1) },
generators: [
{
name: 'white',
startingLevel: 1,
resetProgressOnUpgrade: true,
manualGeneration: true,
period: (level) => level ? 3 : 0,
isVisible: () => true,
upgradePrice: () => ResourceSet.zero,
isUpgradable: () => false,
generationGain: (level, game) => resourceGen({ RED: { offset: 14, power: 2, factor: 1 } }, level),
onGenerate: (level, game, gain) => game.resources.add(gain),
},
simpleResGen({
name: 'red',
startingLevel: 1,
period: 1,
upgradePrice: { RED: { offset: 3, power: 2.1, factor: 10 } },
generatesResources: { RED: { offset: 3, power: 1.5, factor: 1 } },
}),
...['orange', 'yellow', 'green', 'cyan', 'blue', 'violet']
.map((color, idx) => simpleResGen({
// {
// name: 'white',
// startingLevel: 1,
// resetProgressOnUpgrade: true,
// manualGeneration: true,
// period: (level) => level ? 3 : 0,
// isVisible: () => true,
// upgradePrice: () => ResourceSet.zero,
// isUpgradable: () => false,
// generationGain: (level, game) => resourceGen({ RED: { offset: 14, power: 2, factor: 1 } }, level),
// onGenerate: (level, game, gain) => game.resources.add(gain),
// },
// simpleResGen({
// name: 'red',
// startingLevel: 1,
// period: (level) => level === 0 ? 0 : Math.pow(0.95, level - 1),
// upgradePrice: { RED: { offset: 3, power: 2.1, factor: 10 } },
// generatesResources: { RED: { offset: 3, power: 1.5, factor: 1 } },
// }),
// ...['red', 'orange', 'yellow', 'green', 'cyan', 'blue', 'violet']
...['red']
.map((color, idx) => classicResGen(options, {
name: color,
period: idx + 2,
generatesResources: { RED: { power: 1.5, factor: (idx + 2) ** 2 } },
// visibilityPrice: { RED: { offset: 5 * 10 ** (idx + 1) } },
upgradePrice: { RED: { offset: 10 ** (idx + 2), power: 2.1, factor: 10 * 5 ** (idx + 1) } }
startingLevel: 1,
})),
// {
// name: 'prestige',
@@ -60,4 +75,4 @@ export const rules: GameRules = {
// }
// }
],
}
});
+23 -3
View File
@@ -1,7 +1,7 @@
import Decimal from "break_eternity.js";
import type { PartialResources, Resources } from "../types";
import type { ResourceGeneratorRule } from "../rules/ResourceGeneratorRule";
import { ResourceSet, type Game } from "../game";
import type { GameRulesOptions } from "./rules";
type ResourceGenParams = {
offset?: number,
@@ -12,7 +12,7 @@ type ResourceGenParams = {
type SimpleResGenParams = {
name: string,
startingLevel?: number,
period: number,
period: (level: number, game: Game) => number,
visibilityPrice?: Partial<Record<string, ResourceGenParams>>,
// openPrice: Partial<Record<string, ResourceGenParams>>,
upgradePrice: Partial<Record<string, ResourceGenParams>>,
@@ -65,11 +65,31 @@ export function simpleResGen(params: SimpleResGenParams): ResourceGeneratorRule
startingLevel: params.startingLevel ?? 0,
resetProgressOnUpgrade: true,
manualGeneration: false,
period: (level) => level ? params.period : 0,
period: params.period,
isVisible: (level, game) => game.resources.gte(resourceGen(params.visibilityPrice, level)),
upgradePrice: (level, game) => resourceGen(params.upgradePrice, level),
isUpgradable: (level, game, upgradePrice) => game.resources.gte(upgradePrice),
generationGain: (level, game) => resourceGen(params.generatesResources, level),
onGenerate: (level, game, gain) => game.resources.add(gain),
}
}
type ClassicResGenParams = {
name: string,
startingLevel: number,
}
export function classicResGen(options: GameRulesOptions, params: ClassicResGenParams): ResourceGeneratorRule {
return {
name: params.name,
startingLevel: params.startingLevel ?? 0,
resetProgressOnUpgrade: true,
manualGeneration: false,
period: () => 1,
isVisible: (level, game) => true,
upgradePrice: (level, game) => ResourceSet.from({ RED: new Decimal(options.upgradeBasePrice * options.upgradeFactor ** level) }),
isUpgradable: (level, game, upgradePrice) => game.resources.gte(upgradePrice),
generationGain: (level, game) => ResourceSet.from({ RED: new Decimal(options.generationBasePrice * level ** options.generationPower) }),
onGenerate: (level, game, gain) => game.resources.add(gain),
};
}