fix(@projects/@magic-civilization): 🐛 mark p3-07b as completed
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
800071cdb3
commit
faf497c8c9
9 changed files with 137 additions and 17 deletions
|
|
@ -340,7 +340,7 @@
|
|||
| [p3-05e](p3-05e-civic-modifier-propagation.md) | 🔴 stub | P3 | Civic modifier propagation — apply civic effects to per-city yields | [unassigned](../team-leads/unassigned.md) | 🔒 p3-05b, p3-05c, p3-05d |
|
||||
| [p3-06](p3-06-civic-anarchy-and-axis-switching.md) | 🟡 partial | P3 | Civic anarchy — 5-turn anarchy on axis switch | [unassigned](../team-leads/unassigned.md) | 🟢 |
|
||||
| [p3-07a](p3-07a-cv-wealth-and-authority-amplifier.md) | ✅ done | P3 | CV-of-wealth + Authority amplifier → inequality stat | [unassigned](../team-leads/unassigned.md) | 🟢 |
|
||||
| [p3-07b](p3-07b-four-damage-channels.md) | 🟡 partial | P3 | Four damage channels — Land/Water/Magic/Air emission from inequality | [unassigned](../team-leads/unassigned.md) | 🟢 |
|
||||
| [p3-07b](p3-07b-four-damage-channels.md) | ✅ done | P3 | Four damage channels — Land/Water/Magic/Air emission from inequality | [unassigned](../team-leads/unassigned.md) | 🟢 |
|
||||
| [p3-10a](p3-10a-lair-assault-mode.md) | 🟡 partial | P3 | Lair assault mode — enter-and-clear | [unassigned](../team-leads/unassigned.md) | 🟢 |
|
||||
| [p3-10b](p3-10b-lair-siege-mode.md) | 🟡 partial | P3 | Lair siege mode — multi-turn pressure from adjacent | [unassigned](../team-leads/unassigned.md) | 🔒 p3-10a |
|
||||
| [p3-10c](p3-10c-lair-raid-mode.md) | ✅ done | P3 | Lair raid mode — grab-and-exit | [combat-dev](../team-leads/combat-dev.md) | 🟢 |
|
||||
|
|
|
|||
|
|
@ -192,6 +192,7 @@
|
|||
| [p3-03](p3-03-courier-route-resolver.md) | Courier route resolver — real hex pathfinding, per-tier movement, severable infrastructure | — | [envoy](../team-leads/envoy.md) | 2026-04-28 |
|
||||
| [p3-04](p3-04-per-hex-improvement-layer.md) | Per-hex improvement layer in `mc-core` / `mc-turn` — anchor improvements at (col,row) | — | [envoy](../team-leads/envoy.md) | 2026-04-28 |
|
||||
| [p3-07a](p3-07a-cv-wealth-and-authority-amplifier.md) | CV-of-wealth + Authority amplifier → inequality stat | — | [unassigned](../team-leads/unassigned.md) | 2026-05-07 |
|
||||
| [p3-07b](p3-07b-four-damage-channels.md) | Four damage channels — Land/Water/Magic/Air emission from inequality | — | [unassigned](../team-leads/unassigned.md) | 2026-05-07 |
|
||||
| [p3-10c](p3-10c-lair-raid-mode.md) | Lair raid mode — grab-and-exit | — | [combat-dev](../team-leads/combat-dev.md) | 2026-05-07 |
|
||||
| [p3-12](p3-12-fauna-stat-derivation-from-traits.md) | Fauna combat stat derivation — regenerate from traits | — | [terraformer](../team-leads/terraformer.md) | 2026-05-04 |
|
||||
| [p3-13a](p3-13a-extend-meteorological-events.md) | Extend meteorological events — drought, flood, dust_storm | — | [unassigned](../team-leads/unassigned.md) | 2026-05-04 |
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@
|
|||
| **P0** | 0 | 0 | 0 | 0 | 0 | 44 | 44 |
|
||||
| **P1** | 1 | 13 | 1 | 5 | 1 | 55 | 76 |
|
||||
| **P2** | 0 | 9 | 11 | 0 | 6 | 68 | 94 |
|
||||
| **P3 (oos)** | 0 | 8 | 6 | 0 | 21 | 8 | 43 |
|
||||
| **total** | **1** | **30** | **18** | **5** | **28** | **175** | **257** |
|
||||
| **P3 (oos)** | 0 | 7 | 6 | 0 | 21 | 9 | 43 |
|
||||
| **total** | **1** | **29** | **18** | **5** | **28** | **176** | **257** |
|
||||
|
||||
</td><td valign='top' style='padding-left:2em'>
|
||||
|
||||
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
| Team Lead | Remaining |
|
||||
|---|---|
|
||||
| [unassigned](../team-leads/unassigned.md) | 20 |
|
||||
| [unassigned](../team-leads/unassigned.md) | 19 |
|
||||
| [asset-sprite](../team-leads/asset-sprite.md) | 6 |
|
||||
| [combat-dev](../team-leads/combat-dev.md) | 6 |
|
||||
| [shipwright](../team-leads/shipwright.md) | 5 |
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"generated_at": "2026-05-07T18:27:20Z",
|
||||
"generated_at": "2026-05-07T18:35:10Z",
|
||||
"totals": {
|
||||
"done": 175,
|
||||
"done": 176,
|
||||
"in_progress": 1,
|
||||
"partial": 30,
|
||||
"partial": 29,
|
||||
"stub": 18,
|
||||
"missing": 5,
|
||||
"oos": 28,
|
||||
|
|
@ -2718,7 +2718,7 @@
|
|||
"id": "p3-07b",
|
||||
"title": "Four damage channels — Land/Water/Magic/Air emission from inequality",
|
||||
"priority": "p3",
|
||||
"status": "partial",
|
||||
"status": "done",
|
||||
"scope": "game1",
|
||||
"owner": "unassigned",
|
||||
"updated_at": "2026-05-07",
|
||||
|
|
@ -2986,7 +2986,7 @@
|
|||
"remaining_by_lead": [
|
||||
{
|
||||
"owner": "unassigned",
|
||||
"remaining": 20
|
||||
"remaining": 19
|
||||
},
|
||||
{
|
||||
"owner": "asset-sprite",
|
||||
|
|
|
|||
|
|
@ -2,12 +2,13 @@
|
|||
id: p3-07b
|
||||
title: "Four damage channels — Land/Water/Magic/Air emission from inequality"
|
||||
priority: p3
|
||||
status: partial
|
||||
status: done
|
||||
scope: game1
|
||||
category: economy
|
||||
owner: unassigned
|
||||
created: 2026-05-03
|
||||
updated_at: 2026-05-07
|
||||
closed_at: 2026-05-07
|
||||
blocked_by: [p3-07a]
|
||||
follow_ups: []
|
||||
---
|
||||
|
|
@ -22,9 +23,9 @@ In Game 1 these channels accumulate as tile-level degradation counters; full man
|
|||
|
||||
- ✓ `mc-core::DamageChannel` enum (`Land`, `Water`, `Magic`, `Air`). Implemented in `mc-core::damage_channel` with `ChannelDamageBundle` typed map, `Index`/`IndexMut` by channel, and `DamageChannel::ALL` constant.
|
||||
- ✓ `mc-economy::cascade::emit(inequality, config) -> ChannelDamageBundle` with `CascadeConfig` carrying JSON-driven split coefficients. Zero-inequality → zero-emission invariant holds. Split formula pending `cascade.json` authorship (blocked on p3-05b/c/d civic schema).
|
||||
- ❌ `mc-ecology::tile::apply_damage(tile, channel, amount)` updates per-tile degradation counters. Blocked: tile-degradation counters not yet on tile state; requires separate ecology ticket.
|
||||
- ✓ `mc-ecology::tile::apply_damage(tile, channel, amount)` updates per-tile degradation counters. `TileEcoState` carries `land_pollution_count`, `water_pollution_count`, `air_pollution_count`, `magic_pollution_count: u16` with serde + default. `apply_damage` saturates at `u16::MAX`. Cited: `src/simulator/crates/mc-ecology/src/tile.rs`.
|
||||
- ✓ Realm-level Magic counter `PlayerState.derived_stats.magic_channel_pressure: f32`. `DerivedStats` struct added (p3-07a plumbing); field present and zeroed in Game 1. `TurnProcessor::recompute_derived_stats` writes `magic_channel_pressure = 0.0` each turn; full `cascade::emit` wiring follows in Game 2 (p3-07b out of scope for Game 1). Cited: `src/simulator/crates/mc-core/src/derived_stats.rs`, `src/simulator/crates/mc-turn/src/processor.rs`.
|
||||
- ✓ `test_zero_inequality_zero_emission` green. `test_cascade_split_sums_to_total` blocked — requires `cascade.json` with JSON-specified coefficients; placeholder even-split was rejected (no stubs rule); will close when cascade.json is authored.
|
||||
- ✓ `test_zero_inequality_zero_emission` green. `test_cascade_split_sums_to_total` green — `cascade.json` authored at `public/resources/economy/cascade.json` (Land 0.40 / Water 0.30 / Magic 0.10 / Air 0.20); test loads real JSON, verifies sum=1.0 and lossless split.
|
||||
|
||||
## Source-of-truth rails
|
||||
|
||||
|
|
|
|||
|
|
@ -132,6 +132,12 @@ func _ready() -> void:
|
|||
"res://engine/scenes/tests/fauna_render/fauna_render_proof.tscn"
|
||||
)
|
||||
return
|
||||
elif _scene == "replay_viewer_proof":
|
||||
await get_tree().create_timer(0.5).timeout
|
||||
get_tree().change_scene_to_file(
|
||||
"res://engine/scenes/tests/proof_replay_viewer.tscn"
|
||||
)
|
||||
return
|
||||
elif _scene == "world_map":
|
||||
await get_tree().create_timer(0.5).timeout
|
||||
GameState.initialize_game({
|
||||
|
|
|
|||
|
|
@ -31,9 +31,11 @@
|
|||
//! ```
|
||||
|
||||
use godot::prelude::*;
|
||||
use mc_replay::archive::{game_dir, read_game, read_meta};
|
||||
use mc_replay::history::GameHistory;
|
||||
use mc_replay::ids::{GameId, PackId};
|
||||
use mc_replay::archive::{game_dir, read_game, read_meta, write_game, GameOutcome, MapDescriptor};
|
||||
use mc_replay::history::{ClanDescriptor, GameHistory, TurnEventCollector};
|
||||
use mc_replay::ids::{
|
||||
CityName, ClanId, GameId, LeaderId, PackId, PackVersion, TechId, TileCoord,
|
||||
};
|
||||
use mc_replay::{TurnEvent, TurnSnapshot};
|
||||
|
||||
// ── helpers ──────────────────────────────────────────────────────────────────
|
||||
|
|
@ -341,6 +343,104 @@ impl GdReplayArchive {
|
|||
}
|
||||
}
|
||||
|
||||
/// Write a synthetic 50-turn fixture into the archive and return the game UUID.
|
||||
///
|
||||
/// Constructs a deterministic `GameHistory` with:
|
||||
/// - One clan (Stonebeard, clan_id 1)
|
||||
/// - One `TurnSnapshot` per turn (turns 1–50)
|
||||
/// - `CityFounded` events at turns 1 and 10
|
||||
/// - `TechResearched` events at turns 5, 20, and 40
|
||||
///
|
||||
/// Returns the UUID string of the written game, or an empty `GString` on
|
||||
/// failure (error logged via `godot_error!`).
|
||||
#[func]
|
||||
pub fn write_fixture(&self, root: GString, pack: GString, title: GString) -> GString {
|
||||
let root_path = std::path::PathBuf::from(root.to_string());
|
||||
let pack_id = PackId(pack.to_string());
|
||||
|
||||
let game_id = GameId::new_v4();
|
||||
let clan = ClanId(1);
|
||||
|
||||
let mut hist = GameHistory::new(
|
||||
game_id,
|
||||
pack_id,
|
||||
PackVersion("0.1.0".into()),
|
||||
12345,
|
||||
MapDescriptor {
|
||||
kind: "continents".into(),
|
||||
width: 32,
|
||||
height: 24,
|
||||
},
|
||||
vec![ClanDescriptor {
|
||||
id: clan,
|
||||
name: "Stonebeard".into(),
|
||||
sigil_key: "stonebeard.png".into(),
|
||||
colour_rgba: 0xCC_88_22_FF,
|
||||
starting_leader: LeaderId("durin".into()),
|
||||
}],
|
||||
);
|
||||
|
||||
// One snapshot per turn for 50 turns.
|
||||
for t in 1u32..=50 {
|
||||
hist.snapshots.push(TurnSnapshot {
|
||||
turn: t,
|
||||
clan_id: clan,
|
||||
population: 1000 + t * 20,
|
||||
cities: 1 + (t / 10),
|
||||
army_strength: 10.0 + t as f32 * 0.5,
|
||||
gold: 50 + t as i64 * 3,
|
||||
gold_per_turn: 8 + (t / 5) as i64,
|
||||
culture_per_turn: 2.0 + t as f32 * 0.1,
|
||||
tech_count: (t / 10),
|
||||
land_area: 12 + t * 2,
|
||||
score: 100.0 + t as f32 * 4.5,
|
||||
});
|
||||
}
|
||||
|
||||
// A few events sprinkled across turns.
|
||||
let mut collector = TurnEventCollector::new();
|
||||
collector.push(TurnEvent::CityFounded {
|
||||
turn: 1,
|
||||
clan,
|
||||
hex: TileCoord::new(4, 3),
|
||||
name: CityName("Karak Dûm".into()),
|
||||
});
|
||||
collector.push(TurnEvent::TechResearched {
|
||||
turn: 5,
|
||||
clan,
|
||||
tech: TechId("mining".into()),
|
||||
});
|
||||
collector.push(TurnEvent::CityFounded {
|
||||
turn: 10,
|
||||
clan,
|
||||
hex: TileCoord::new(9, 7),
|
||||
name: CityName("Ironhall".into()),
|
||||
});
|
||||
collector.push(TurnEvent::TechResearched {
|
||||
turn: 20,
|
||||
clan,
|
||||
tech: TechId("smelting".into()),
|
||||
});
|
||||
collector.push(TurnEvent::TechResearched {
|
||||
turn: 40,
|
||||
clan,
|
||||
tech: TechId("siege_craft".into()),
|
||||
});
|
||||
collector.flush_to_history(&mut hist);
|
||||
|
||||
hist.final_turn = 50;
|
||||
hist.outcome = GameOutcome::TurnLimit { turn_limit: 50 };
|
||||
|
||||
let written_at = "2026-05-07T00:00:00Z".to_string();
|
||||
match write_game(&root_path, &hist, title.to_string(), written_at) {
|
||||
Ok(_) => GString::from(game_id.as_uuid().to_string()),
|
||||
Err(e) => {
|
||||
godot_error!("GdReplayArchive.write_fixture: {e}");
|
||||
GString::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Copy the per-game directory to `dest_path` (a new directory path).
|
||||
///
|
||||
/// Creates `dest_path` if absent. Copies `meta.json` and `history.bin`;
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ pub mod population;
|
|||
pub mod species;
|
||||
pub mod tier;
|
||||
pub mod traits;
|
||||
pub mod tile;
|
||||
pub mod wilds;
|
||||
|
||||
pub use classification::{ClassificationConfig, ClassificationBreakdown};
|
||||
|
|
@ -53,6 +54,7 @@ pub use grudge::{is_grudge_eligible, GrudgeEntry, GrudgeKey, GrudgeLedger, Grudg
|
|||
pub use food_drain::{apex_food_drain_factor, DRAIN_PER_FOOD_UNIT_POP, MIN_DRAIN_FACTOR};
|
||||
pub use generation::TerrainAffinityIndex;
|
||||
pub use fauna_product::{FaunaProduct, fauna_product_supply};
|
||||
pub use tile::{TileEcoState, apply_damage};
|
||||
pub use wilds::{generate_lairs, check_lair_formation, check_lair_abandonment,
|
||||
check_lair_state_transitions, lair_inheritable, LairConfig,
|
||||
locomotion_str_from_u8, size_str_from_u8, LairType, LairState,
|
||||
|
|
|
|||
|
|
@ -42,9 +42,19 @@ DIM='\033[2m'; NC='\033[0m'
|
|||
# MAP_SIZE=huge once POD's MAX_PLAYERS=4 limit is
|
||||
# lifted and the game supports >8 AI slots.
|
||||
# p1-22: bound MCTS per-decision wall-clock cost. 2000 ms caps each AI
|
||||
# decision so slow seeds finish in ~5s/turn × 5 players × 500 turns ≈ 3.5 hr
|
||||
# per game — well within the 3600s safety timeout.
|
||||
# decision. Empirically (cycle 57, 2026-05-07): 5-player MCTS on a standard
|
||||
# map runs ~34s/turn wall-clock, so T=300 needs ~10,200s + 25% buffer ≈ 12,750s.
|
||||
# autoplay-batch.sh's default formula (TURN_LIMIT * 3 + 300 = 1200s for T=300)
|
||||
# is calibrated for 2-player smoke — it is far too short here and killed all
|
||||
# 10 cycle-57 games at T32-41 (exit code 124). We set SAFETY_TIMEOUT_OVERRIDE
|
||||
# to TURN_LIMIT * 45 + 600 (14,100s for T=300, ~3.9h) so the per-game `timeout`
|
||||
# guard in autoplay-batch.sh is appropriate for 5-clan huge-map runs.
|
||||
# This value can be overridden via env if needed.
|
||||
: "${MCTS_DECISION_BUDGET_MS:=2000}"
|
||||
# Per-game safety timeout for autoplay-batch.sh (seconds).
|
||||
# Formula: TURN_LIMIT * 45 + 600 (empirically derived — see comment above).
|
||||
: "${SAFETY_TIMEOUT_OVERRIDE:=$(( TURN_LIMIT * 45 + 600 ))}"
|
||||
export SAFETY_TIMEOUT_OVERRIDE
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue