@its-not-rocket-science/ananke 0.1.39 → 0.1.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/dist/src/resources.d.ts +149 -0
- package/dist/src/resources.js +219 -0
- package/package.json +5 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,29 @@ Versioning follows [Semantic Versioning](https://semver.org/).
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
## [0.1.40] — 2026-03-26
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **Phase 95 · Natural Resources & Extraction** (`src/resources.ts`)
|
|
14
|
+
- `ResourceType`: `"iron" | "silver" | "timber" | "stone" | "horses"`.
|
|
15
|
+
- `ResourceDeposit { depositId, polityId, type, richness_Q, maxWorkers }` — immutable site descriptor.
|
|
16
|
+
- `ExtractionState { depositId, assignedWorkers, cumulativeYield_cu }` — mutable accumulator stored externally.
|
|
17
|
+
- `BASE_YIELD_PER_WORKER: Record<ResourceType, number>` — silver 8, horses 5, iron 3, timber/stone 2 cu/worker/day at base.
|
|
18
|
+
- `TECH_EXTRACTION_MUL: Record<number, Q>` — numeric TechEra keys; Prehistoric q(0.40) → DeepSpace q(4.00).
|
|
19
|
+
- `computeDailyYield(deposit, state, techEra)` → cu/day: `workers × baseRate × techMul × richnessMul`; `richnessMul ∈ [q(0.50), q(1.00)]`; 0 when exhausted or no workers.
|
|
20
|
+
- `assignWorkers(deposit, state, workers)` — clamps to `[0, deposit.maxWorkers]`.
|
|
21
|
+
- `depleteDeposit(deposit, yield_cu)` — reduces `richness_Q` by `DEPLETION_RATE_PER_1000_CU = q(0.005)` per 1000 cu extracted.
|
|
22
|
+
- `stepExtraction(deposit, state, polity, elapsedDays)` → `ExtractionStepResult`: adds yield to `polity.treasury_cu`; depletes richness; returns `{ yield_cu, richness_Q, exhausted }`.
|
|
23
|
+
- `computeTotalDailyResourceIncome(deposits, states, techEra)` → cu/day total across all deposits.
|
|
24
|
+
- Secondary bonus sets: `MILITARY_BONUS_RESOURCES` (iron, horses), `CONSTRUCTION_BONUS_RESOURCES` (timber, stone), `MOBILITY_BONUS_RESOURCES` (horses) — advisory flags for Phase-61/89/93.
|
|
25
|
+
- `hasMilitaryBonus / hasConstructionBonus / hasMobilityBonus` helpers.
|
|
26
|
+
- `estimateDaysToExhaustion(deposit, state, techEra)` → ceiling days; Infinity with no workers; 0 when already exhausted.
|
|
27
|
+
- Added `./resources` subpath export to `package.json`.
|
|
28
|
+
- 49 new tests; 4,981 total. Coverage maintained above all thresholds.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
9
32
|
## [0.1.39] — 2026-03-26
|
|
10
33
|
|
|
11
34
|
### Added
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import type { Q } from "./units.js";
|
|
2
|
+
import type { Polity } from "./polity.js";
|
|
3
|
+
/** Classification of natural resource. */
|
|
4
|
+
export type ResourceType = "iron" | "silver" | "timber" | "stone" | "horses";
|
|
5
|
+
/** Immutable descriptor for a natural resource deposit. */
|
|
6
|
+
export interface ResourceDeposit {
|
|
7
|
+
depositId: string;
|
|
8
|
+
polityId: string;
|
|
9
|
+
type: ResourceType;
|
|
10
|
+
/**
|
|
11
|
+
* Initial richness [0, SCALE.Q]. Declines with extraction via `depleteDeposit`.
|
|
12
|
+
* A richness of SCALE.Q represents an exceptionally rich find.
|
|
13
|
+
*/
|
|
14
|
+
richness_Q: Q;
|
|
15
|
+
/**
|
|
16
|
+
* Maximum workers that can be productively assigned to this deposit.
|
|
17
|
+
* Additional workers beyond this are wasted.
|
|
18
|
+
*/
|
|
19
|
+
maxWorkers: number;
|
|
20
|
+
}
|
|
21
|
+
/** Mutable extraction state — store one externally per deposit per polity. */
|
|
22
|
+
export interface ExtractionState {
|
|
23
|
+
depositId: string;
|
|
24
|
+
/** Current worker count (may not exceed `deposit.maxWorkers`). */
|
|
25
|
+
assignedWorkers: number;
|
|
26
|
+
/** Cumulative cost-units yielded since deposit was first worked. */
|
|
27
|
+
cumulativeYield_cu: number;
|
|
28
|
+
}
|
|
29
|
+
/** Output of `stepExtraction`. */
|
|
30
|
+
export interface ExtractionStepResult {
|
|
31
|
+
/** Cost-units added to polity treasury this step. */
|
|
32
|
+
yield_cu: number;
|
|
33
|
+
/** Current richness after any depletion [0, SCALE.Q]. */
|
|
34
|
+
richness_Q: Q;
|
|
35
|
+
/**
|
|
36
|
+
* Whether the deposit is now exhausted (`richness_Q <= DEPLETION_EXHAUSTED_Q`).
|
|
37
|
+
*/
|
|
38
|
+
exhausted: boolean;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Base daily yield per worker [cost-units/worker/day] at full richness and
|
|
42
|
+
* base tech era (Ancient = 1, the reference point).
|
|
43
|
+
*/
|
|
44
|
+
export declare const BASE_YIELD_PER_WORKER: Record<ResourceType, number>;
|
|
45
|
+
/**
|
|
46
|
+
* Tech era extraction efficiency multiplier [Q].
|
|
47
|
+
* Better tools, techniques, and logistics improve yield per worker.
|
|
48
|
+
*/
|
|
49
|
+
export declare const TECH_EXTRACTION_MUL: Record<number, Q>;
|
|
50
|
+
/**
|
|
51
|
+
* Fraction of base yield that richness scales against [Q].
|
|
52
|
+
* At richness q(0.50), yield = base × (0.50 + 0.50×0.50) = base × 0.75
|
|
53
|
+
* — partial depletion still produces meaningful income.
|
|
54
|
+
*/
|
|
55
|
+
export declare const RICHNESS_FLOOR_Q: Q;
|
|
56
|
+
/**
|
|
57
|
+
* Richness reduction per 1000 cost-units of cumulative yield.
|
|
58
|
+
* Controls the depletion rate. Lower values mean longer-lived deposits.
|
|
59
|
+
*/
|
|
60
|
+
export declare const DEPLETION_RATE_PER_1000_CU: Q;
|
|
61
|
+
/**
|
|
62
|
+
* Richness threshold below which the deposit is considered exhausted [Q].
|
|
63
|
+
* Extraction becomes uneconomical below this level.
|
|
64
|
+
*/
|
|
65
|
+
export declare const DEPLETION_EXHAUSTED_Q: Q;
|
|
66
|
+
/**
|
|
67
|
+
* Maximum fraction of polity population that can be assigned as resource
|
|
68
|
+
* workers without impacting farming/tax base [Q].
|
|
69
|
+
* Hosts should warn if this is exceeded.
|
|
70
|
+
*/
|
|
71
|
+
export declare const WORKER_POP_FRACTION_Q: Q;
|
|
72
|
+
/**
|
|
73
|
+
* Resource types that provide a military equipment bonus when worked.
|
|
74
|
+
* Hosts apply this to Phase-61 `deriveMilitaryStrength` or Phase-93 strength.
|
|
75
|
+
*/
|
|
76
|
+
export declare const MILITARY_BONUS_RESOURCES: ReadonlySet<ResourceType>;
|
|
77
|
+
/**
|
|
78
|
+
* Resource types that provide a construction cost discount when worked.
|
|
79
|
+
* Hosts apply this to Phase-89 `investInProject` cost calculations.
|
|
80
|
+
*/
|
|
81
|
+
export declare const CONSTRUCTION_BONUS_RESOURCES: ReadonlySet<ResourceType>;
|
|
82
|
+
/**
|
|
83
|
+
* Resource types that improve march rate when worked.
|
|
84
|
+
* Hosts add a road-equivalent bonus to Phase-93 `stepCampaignMarch`.
|
|
85
|
+
*/
|
|
86
|
+
export declare const MOBILITY_BONUS_RESOURCES: ReadonlySet<ResourceType>;
|
|
87
|
+
/** Create a new `ResourceDeposit`. */
|
|
88
|
+
export declare function createDeposit(depositId: string, polityId: string, type: ResourceType, richness_Q?: Q, maxWorkers?: number): ResourceDeposit;
|
|
89
|
+
/** Create a fresh `ExtractionState` with no workers assigned. */
|
|
90
|
+
export declare function createExtractionState(depositId: string): ExtractionState;
|
|
91
|
+
/**
|
|
92
|
+
* Assign workers to a deposit.
|
|
93
|
+
* Clamps to `[0, deposit.maxWorkers]`.
|
|
94
|
+
* Returns the effective worker count after clamping.
|
|
95
|
+
*/
|
|
96
|
+
export declare function assignWorkers(deposit: ResourceDeposit, state: ExtractionState, workers: number): number;
|
|
97
|
+
/**
|
|
98
|
+
* Compute the daily extraction yield [cost-units/day].
|
|
99
|
+
*
|
|
100
|
+
* Formula:
|
|
101
|
+
* techMul = TECH_EXTRACTION_MUL[techEra] (default q(0.60))
|
|
102
|
+
* richnessScale = RICHNESS_FLOOR_Q + mulDiv(SCALE.Q - RICHNESS_FLOOR_Q, richness_Q, SCALE.Q)
|
|
103
|
+
* ∈ [q(0.50), q(1.00)]
|
|
104
|
+
* base = workers × BASE_YIELD_PER_WORKER[type]
|
|
105
|
+
* daily = max(0, round(base × techMul / SCALE.Q × richnessScale / SCALE.Q))
|
|
106
|
+
*
|
|
107
|
+
* Returns 0 if the deposit is exhausted or no workers assigned.
|
|
108
|
+
*/
|
|
109
|
+
export declare function computeDailyYield(deposit: ResourceDeposit, state: ExtractionState, techEra: number): number;
|
|
110
|
+
/**
|
|
111
|
+
* Reduce deposit richness based on cumulative yield extracted.
|
|
112
|
+
*
|
|
113
|
+
* `richnessDrain = round(yield_cu × DEPLETION_RATE_PER_1000_CU / 1000)`
|
|
114
|
+
*
|
|
115
|
+
* Mutates `deposit.richness_Q`.
|
|
116
|
+
*/
|
|
117
|
+
export declare function depleteDeposit(deposit: ResourceDeposit, yieldThisStep_cu: number): void;
|
|
118
|
+
/**
|
|
119
|
+
* Advance extraction for `elapsedDays` days.
|
|
120
|
+
*
|
|
121
|
+
* 1. Computes `computeDailyYield × elapsedDays`.
|
|
122
|
+
* 2. Adds yield to `polity.treasury_cu` and `state.cumulativeYield_cu`.
|
|
123
|
+
* 3. Depletes deposit richness proportional to yield.
|
|
124
|
+
*
|
|
125
|
+
* Mutates `polity.treasury_cu`, `state.cumulativeYield_cu`, and `deposit.richness_Q`.
|
|
126
|
+
*/
|
|
127
|
+
export declare function stepExtraction(deposit: ResourceDeposit, state: ExtractionState, polity: Polity, elapsedDays: number): ExtractionStepResult;
|
|
128
|
+
/**
|
|
129
|
+
* Estimate daily bonus income from resource extraction across multiple deposits.
|
|
130
|
+
* Useful for treasury planning alongside Phase-92 tax revenue.
|
|
131
|
+
*/
|
|
132
|
+
export declare function computeTotalDailyResourceIncome(deposits: ResourceDeposit[], states: Map<string, ExtractionState>, techEra: number): number;
|
|
133
|
+
/**
|
|
134
|
+
* Return true if this resource type provides a military bonus.
|
|
135
|
+
*/
|
|
136
|
+
export declare function hasMilitaryBonus(type: ResourceType): boolean;
|
|
137
|
+
/**
|
|
138
|
+
* Return true if this resource type provides a construction bonus.
|
|
139
|
+
*/
|
|
140
|
+
export declare function hasConstructionBonus(type: ResourceType): boolean;
|
|
141
|
+
/**
|
|
142
|
+
* Return true if this resource type provides a mobility bonus.
|
|
143
|
+
*/
|
|
144
|
+
export declare function hasMobilityBonus(type: ResourceType): boolean;
|
|
145
|
+
/**
|
|
146
|
+
* Estimate how many days until the deposit is exhausted at the current
|
|
147
|
+
* extraction rate. Returns `Infinity` if no workers or already exhausted.
|
|
148
|
+
*/
|
|
149
|
+
export declare function estimateDaysToExhaustion(deposit: ResourceDeposit, state: ExtractionState, techEra: number): number;
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
// src/resources.ts — Phase 95: Natural Resources & Extraction
|
|
2
|
+
//
|
|
3
|
+
// Polity economies are not purely population-driven. Mines, forests, quarries,
|
|
4
|
+
// and pastures provide resource income that is largely independent of population
|
|
5
|
+
// size. This module tracks resource deposits, worker assignment, daily yield,
|
|
6
|
+
// and gradual depletion.
|
|
7
|
+
//
|
|
8
|
+
// Design:
|
|
9
|
+
// - Pure data layer — no Entity fields, no kernel changes.
|
|
10
|
+
// - `ResourceDeposit` is the immutable site descriptor; `ExtractionState` is
|
|
11
|
+
// the mutable accumulator stored externally by the host.
|
|
12
|
+
// - Yield scales with workers, tech era, and deposit richness.
|
|
13
|
+
// - Richness slowly declines with cumulative extraction (depletion model).
|
|
14
|
+
// - Resource income is expressed in treasury cost-units for uniform integration
|
|
15
|
+
// with Phase-92 taxation and Phase-89 infrastructure.
|
|
16
|
+
// - Secondary bonus flags (`militaryBonus`, `constructionBonus`, `mobilityBonus`)
|
|
17
|
+
// are advisory — the host applies them to Phase-61/89/93 calls.
|
|
18
|
+
//
|
|
19
|
+
// Integration:
|
|
20
|
+
// Phase 11 (Tech): techEra multiplier improves extraction efficiency.
|
|
21
|
+
// Phase 61 (Polity): treasury_cu receives daily yield; population caps workers.
|
|
22
|
+
// Phase 89 (Infra): timber/stone → construction discount advisory flag.
|
|
23
|
+
// Phase 93 (Campaign): horses → march-rate advisory flag.
|
|
24
|
+
// Phase 92 (Taxation): resource income is additive to tax revenue.
|
|
25
|
+
import { q, SCALE, clampQ, mulDiv } from "./units.js";
|
|
26
|
+
import { TechEra } from "./sim/tech.js";
|
|
27
|
+
// ── Constants ─────────────────────────────────────────────────────────────────
|
|
28
|
+
/**
|
|
29
|
+
* Base daily yield per worker [cost-units/worker/day] at full richness and
|
|
30
|
+
* base tech era (Ancient = 1, the reference point).
|
|
31
|
+
*/
|
|
32
|
+
export const BASE_YIELD_PER_WORKER = {
|
|
33
|
+
iron: 3, // ore smelting; military equipment
|
|
34
|
+
silver: 8, // coinage; highest raw value
|
|
35
|
+
timber: 2, // construction material
|
|
36
|
+
stone: 2, // construction material
|
|
37
|
+
horses: 5, // breeding; cavalry
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Tech era extraction efficiency multiplier [Q].
|
|
41
|
+
* Better tools, techniques, and logistics improve yield per worker.
|
|
42
|
+
*/
|
|
43
|
+
export const TECH_EXTRACTION_MUL = {
|
|
44
|
+
[TechEra.Prehistoric]: q(0.40),
|
|
45
|
+
[TechEra.Ancient]: q(0.60),
|
|
46
|
+
[TechEra.Medieval]: q(0.80),
|
|
47
|
+
[TechEra.EarlyModern]: q(1.00),
|
|
48
|
+
[TechEra.Industrial]: q(1.50),
|
|
49
|
+
[TechEra.Modern]: q(2.00),
|
|
50
|
+
[TechEra.NearFuture]: q(2.50),
|
|
51
|
+
[TechEra.FarFuture]: q(3.00),
|
|
52
|
+
[TechEra.DeepSpace]: q(4.00),
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Fraction of base yield that richness scales against [Q].
|
|
56
|
+
* At richness q(0.50), yield = base × (0.50 + 0.50×0.50) = base × 0.75
|
|
57
|
+
* — partial depletion still produces meaningful income.
|
|
58
|
+
*/
|
|
59
|
+
export const RICHNESS_FLOOR_Q = q(0.50);
|
|
60
|
+
/**
|
|
61
|
+
* Richness reduction per 1000 cost-units of cumulative yield.
|
|
62
|
+
* Controls the depletion rate. Lower values mean longer-lived deposits.
|
|
63
|
+
*/
|
|
64
|
+
export const DEPLETION_RATE_PER_1000_CU = q(0.005);
|
|
65
|
+
/**
|
|
66
|
+
* Richness threshold below which the deposit is considered exhausted [Q].
|
|
67
|
+
* Extraction becomes uneconomical below this level.
|
|
68
|
+
*/
|
|
69
|
+
export const DEPLETION_EXHAUSTED_Q = q(0.05);
|
|
70
|
+
/**
|
|
71
|
+
* Maximum fraction of polity population that can be assigned as resource
|
|
72
|
+
* workers without impacting farming/tax base [Q].
|
|
73
|
+
* Hosts should warn if this is exceeded.
|
|
74
|
+
*/
|
|
75
|
+
export const WORKER_POP_FRACTION_Q = q(0.10);
|
|
76
|
+
// ── Secondary bonus flags ─────────────────────────────────────────────────────
|
|
77
|
+
/**
|
|
78
|
+
* Resource types that provide a military equipment bonus when worked.
|
|
79
|
+
* Hosts apply this to Phase-61 `deriveMilitaryStrength` or Phase-93 strength.
|
|
80
|
+
*/
|
|
81
|
+
export const MILITARY_BONUS_RESOURCES = new Set(["iron", "horses"]);
|
|
82
|
+
/**
|
|
83
|
+
* Resource types that provide a construction cost discount when worked.
|
|
84
|
+
* Hosts apply this to Phase-89 `investInProject` cost calculations.
|
|
85
|
+
*/
|
|
86
|
+
export const CONSTRUCTION_BONUS_RESOURCES = new Set(["timber", "stone"]);
|
|
87
|
+
/**
|
|
88
|
+
* Resource types that improve march rate when worked.
|
|
89
|
+
* Hosts add a road-equivalent bonus to Phase-93 `stepCampaignMarch`.
|
|
90
|
+
*/
|
|
91
|
+
export const MOBILITY_BONUS_RESOURCES = new Set(["horses"]);
|
|
92
|
+
// ── Factory ───────────────────────────────────────────────────────────────────
|
|
93
|
+
/** Create a new `ResourceDeposit`. */
|
|
94
|
+
export function createDeposit(depositId, polityId, type, richness_Q = q(0.80), maxWorkers = 500) {
|
|
95
|
+
return { depositId, polityId, type, richness_Q: clampQ(richness_Q, 0, SCALE.Q), maxWorkers };
|
|
96
|
+
}
|
|
97
|
+
/** Create a fresh `ExtractionState` with no workers assigned. */
|
|
98
|
+
export function createExtractionState(depositId) {
|
|
99
|
+
return { depositId, assignedWorkers: 0, cumulativeYield_cu: 0 };
|
|
100
|
+
}
|
|
101
|
+
// ── Worker management ─────────────────────────────────────────────────────────
|
|
102
|
+
/**
|
|
103
|
+
* Assign workers to a deposit.
|
|
104
|
+
* Clamps to `[0, deposit.maxWorkers]`.
|
|
105
|
+
* Returns the effective worker count after clamping.
|
|
106
|
+
*/
|
|
107
|
+
export function assignWorkers(deposit, state, workers) {
|
|
108
|
+
state.assignedWorkers = Math.max(0, Math.min(workers, deposit.maxWorkers));
|
|
109
|
+
return state.assignedWorkers;
|
|
110
|
+
}
|
|
111
|
+
// ── Yield computation ─────────────────────────────────────────────────────────
|
|
112
|
+
/**
|
|
113
|
+
* Compute the daily extraction yield [cost-units/day].
|
|
114
|
+
*
|
|
115
|
+
* Formula:
|
|
116
|
+
* techMul = TECH_EXTRACTION_MUL[techEra] (default q(0.60))
|
|
117
|
+
* richnessScale = RICHNESS_FLOOR_Q + mulDiv(SCALE.Q - RICHNESS_FLOOR_Q, richness_Q, SCALE.Q)
|
|
118
|
+
* ∈ [q(0.50), q(1.00)]
|
|
119
|
+
* base = workers × BASE_YIELD_PER_WORKER[type]
|
|
120
|
+
* daily = max(0, round(base × techMul / SCALE.Q × richnessScale / SCALE.Q))
|
|
121
|
+
*
|
|
122
|
+
* Returns 0 if the deposit is exhausted or no workers assigned.
|
|
123
|
+
*/
|
|
124
|
+
export function computeDailyYield(deposit, state, techEra) {
|
|
125
|
+
if (deposit.richness_Q <= DEPLETION_EXHAUSTED_Q)
|
|
126
|
+
return 0;
|
|
127
|
+
if (state.assignedWorkers <= 0)
|
|
128
|
+
return 0;
|
|
129
|
+
const techMul = (TECH_EXTRACTION_MUL[techEra] ?? q(0.60));
|
|
130
|
+
const richnessMul = RICHNESS_FLOOR_Q + mulDiv(SCALE.Q - RICHNESS_FLOOR_Q, deposit.richness_Q, SCALE.Q);
|
|
131
|
+
const basePerWorker = BASE_YIELD_PER_WORKER[deposit.type] ?? 0;
|
|
132
|
+
const base = state.assignedWorkers * basePerWorker;
|
|
133
|
+
const withTech = Math.round(base * techMul / SCALE.Q);
|
|
134
|
+
return Math.max(0, Math.round(withTech * richnessMul / SCALE.Q));
|
|
135
|
+
}
|
|
136
|
+
// ── Depletion ─────────────────────────────────────────────────────────────────
|
|
137
|
+
/**
|
|
138
|
+
* Reduce deposit richness based on cumulative yield extracted.
|
|
139
|
+
*
|
|
140
|
+
* `richnessDrain = round(yield_cu × DEPLETION_RATE_PER_1000_CU / 1000)`
|
|
141
|
+
*
|
|
142
|
+
* Mutates `deposit.richness_Q`.
|
|
143
|
+
*/
|
|
144
|
+
export function depleteDeposit(deposit, yieldThisStep_cu) {
|
|
145
|
+
if (yieldThisStep_cu <= 0)
|
|
146
|
+
return;
|
|
147
|
+
const drain = Math.round(yieldThisStep_cu * DEPLETION_RATE_PER_1000_CU / 1000);
|
|
148
|
+
deposit.richness_Q = clampQ(deposit.richness_Q - drain, 0, SCALE.Q);
|
|
149
|
+
}
|
|
150
|
+
// ── Extraction step ───────────────────────────────────────────────────────────
|
|
151
|
+
/**
|
|
152
|
+
* Advance extraction for `elapsedDays` days.
|
|
153
|
+
*
|
|
154
|
+
* 1. Computes `computeDailyYield × elapsedDays`.
|
|
155
|
+
* 2. Adds yield to `polity.treasury_cu` and `state.cumulativeYield_cu`.
|
|
156
|
+
* 3. Depletes deposit richness proportional to yield.
|
|
157
|
+
*
|
|
158
|
+
* Mutates `polity.treasury_cu`, `state.cumulativeYield_cu`, and `deposit.richness_Q`.
|
|
159
|
+
*/
|
|
160
|
+
export function stepExtraction(deposit, state, polity, elapsedDays) {
|
|
161
|
+
const daily = computeDailyYield(deposit, state, polity.techEra);
|
|
162
|
+
const yield_cu = daily * elapsedDays;
|
|
163
|
+
polity.treasury_cu += yield_cu;
|
|
164
|
+
state.cumulativeYield_cu += yield_cu;
|
|
165
|
+
depleteDeposit(deposit, yield_cu);
|
|
166
|
+
const exhausted = deposit.richness_Q <= DEPLETION_EXHAUSTED_Q;
|
|
167
|
+
return { yield_cu, richness_Q: deposit.richness_Q, exhausted };
|
|
168
|
+
}
|
|
169
|
+
// ── Reporting ─────────────────────────────────────────────────────────────────
|
|
170
|
+
/**
|
|
171
|
+
* Estimate daily bonus income from resource extraction across multiple deposits.
|
|
172
|
+
* Useful for treasury planning alongside Phase-92 tax revenue.
|
|
173
|
+
*/
|
|
174
|
+
export function computeTotalDailyResourceIncome(deposits, states, techEra) {
|
|
175
|
+
let total = 0;
|
|
176
|
+
for (const deposit of deposits) {
|
|
177
|
+
const state = states.get(deposit.depositId);
|
|
178
|
+
if (!state)
|
|
179
|
+
continue;
|
|
180
|
+
total += computeDailyYield(deposit, state, techEra);
|
|
181
|
+
}
|
|
182
|
+
return total;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Return true if this resource type provides a military bonus.
|
|
186
|
+
*/
|
|
187
|
+
export function hasMilitaryBonus(type) {
|
|
188
|
+
return MILITARY_BONUS_RESOURCES.has(type);
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Return true if this resource type provides a construction bonus.
|
|
192
|
+
*/
|
|
193
|
+
export function hasConstructionBonus(type) {
|
|
194
|
+
return CONSTRUCTION_BONUS_RESOURCES.has(type);
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Return true if this resource type provides a mobility bonus.
|
|
198
|
+
*/
|
|
199
|
+
export function hasMobilityBonus(type) {
|
|
200
|
+
return MOBILITY_BONUS_RESOURCES.has(type);
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Estimate how many days until the deposit is exhausted at the current
|
|
204
|
+
* extraction rate. Returns `Infinity` if no workers or already exhausted.
|
|
205
|
+
*/
|
|
206
|
+
export function estimateDaysToExhaustion(deposit, state, techEra) {
|
|
207
|
+
if (deposit.richness_Q <= DEPLETION_EXHAUSTED_Q)
|
|
208
|
+
return 0;
|
|
209
|
+
const daily = computeDailyYield(deposit, state, techEra);
|
|
210
|
+
if (daily <= 0)
|
|
211
|
+
return Infinity;
|
|
212
|
+
// richness that needs to be drained = richness_Q - DEPLETION_EXHAUSTED_Q
|
|
213
|
+
// drain per day = daily × DEPLETION_RATE_PER_1000_CU / 1000
|
|
214
|
+
const drainPerDay = daily * DEPLETION_RATE_PER_1000_CU / 1000;
|
|
215
|
+
if (drainPerDay <= 0)
|
|
216
|
+
return Infinity;
|
|
217
|
+
const remainingRichness = deposit.richness_Q - DEPLETION_EXHAUSTED_Q;
|
|
218
|
+
return Math.ceil(remainingRichness / drainPerDay);
|
|
219
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@its-not-rocket-science/ananke",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.40",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Deterministic lockstep-friendly SI-units RPG/physics core (fixed-point TS)",
|
|
6
6
|
"license": "MIT",
|
|
@@ -142,6 +142,10 @@
|
|
|
142
142
|
"./governance": {
|
|
143
143
|
"import": "./dist/src/governance.js",
|
|
144
144
|
"types": "./dist/src/governance.d.ts"
|
|
145
|
+
},
|
|
146
|
+
"./resources": {
|
|
147
|
+
"import": "./dist/src/resources.js",
|
|
148
|
+
"types": "./dist/src/resources.d.ts"
|
|
145
149
|
}
|
|
146
150
|
},
|
|
147
151
|
"files": [
|