feat(victory): ✨ increase grace turns to 100 for war council
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
80c8504f7e
commit
3fcc9d2c30
3 changed files with 52 additions and 3 deletions
|
|
@ -18,7 +18,16 @@ const CityScript: GDScript = preload("res://engine/src/entities/city.gd")
|
|||
## degenerate maps (one player eliminated before the game has had time to
|
||||
## develop) are not interesting and skew metrics — give every game at least
|
||||
## this much runway before a winner can be declared.
|
||||
const VICTORY_GRACE_TURNS: int = 10
|
||||
##
|
||||
## Bumped 2026-04-26 from 10 → 100 (warcouncil p1-29 H2). Round 1 evidence
|
||||
## (`.local/iter/p1-29-{hard,insane}-20260426_194{937,939}/`) showed 7-8/10
|
||||
## games end T48-T200 via early-domination even with 2-3× research_mult,
|
||||
## blocking the user's tier-10-by-T200 target. T100 floor gives every game
|
||||
## ≥T100 of mid-game tech development before a domination victory can fire,
|
||||
## composing with H1's research-speed bumps. Score victory still fires only
|
||||
## at turn-limit so the bimodal failure (T100 instant + T500 stalemate) is
|
||||
## the warning signal to watch for.
|
||||
const VICTORY_GRACE_TURNS: int = 100
|
||||
|
||||
## Fallback turn limit if GameState.game_settings.turn_limit is missing.
|
||||
## Real limit is read from game_settings["turn_limit"] at check time so the
|
||||
|
|
|
|||
|
|
@ -327,7 +327,10 @@ impl TurnProcessor {
|
|||
// Phase 7: victory check — use full VictoryConfig when available,
|
||||
// otherwise fall back to simple city-count check.
|
||||
if let Some(ref vc) = self.victory_config {
|
||||
if let Some((wi, vt)) = crate::victory::check_victory(&state.players, vc) {
|
||||
// Pass `state.turn` so `vc.min_domination_turn` (warcouncil p1-29 H2)
|
||||
// can gate the domination branch in early turns. Other victory
|
||||
// types ignore the turn parameter.
|
||||
if let Some((wi, vt)) = crate::victory::check_victory_at_turn(&state.players, vc, state.turn) {
|
||||
result.winner = Some((wi, vt));
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -147,6 +147,16 @@ pub struct VictoryConfig {
|
|||
/// When true, domination requires controlling *every* opponent capital.
|
||||
/// When false, domination requires eliminating all opponents (no cities).
|
||||
pub domination_requires_all_capitals: bool,
|
||||
/// Earliest turn at which a domination victory can fire. `0` means no
|
||||
/// floor (legacy behavior). When `> 0`, `check_victory` returns `None`
|
||||
/// for the domination branch on any turn `< min_domination_turn` even if
|
||||
/// the capital-capture condition is met. Score, gold, culture, science
|
||||
/// victories are unaffected. Designed to prevent rush-domination from
|
||||
/// cutting games short before mid-game tech development can happen
|
||||
/// (warcouncil p1-29 H2). `#[serde(default)]` so existing
|
||||
/// `victories.json` (without this field) deserializes to `0`.
|
||||
#[serde(default)]
|
||||
pub min_domination_turn: u32,
|
||||
}
|
||||
|
||||
// ── Default victory thresholds ───────────────────────────────────────────────
|
||||
|
|
@ -194,6 +204,7 @@ impl Default for VictoryConfig {
|
|||
],
|
||||
science_cost_base: DEFAULT_SCIENCE_COST_BASE,
|
||||
domination_requires_all_capitals: true,
|
||||
min_domination_turn: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -208,9 +219,23 @@ impl Default for VictoryConfig {
|
|||
pub fn check_victory(
|
||||
players: &[PlayerState],
|
||||
config: &VictoryConfig,
|
||||
) -> Option<(u8, VictoryType)> {
|
||||
check_victory_at_turn(players, config, u32::MAX)
|
||||
}
|
||||
|
||||
/// Like [`check_victory`] but takes the current turn so that
|
||||
/// `config.min_domination_turn` can gate the domination branch. Other victory
|
||||
/// types ignore the turn parameter. Existing call sites that don't track the
|
||||
/// turn use [`check_victory`] which passes `u32::MAX` (no floor effect).
|
||||
pub fn check_victory_at_turn(
|
||||
players: &[PlayerState],
|
||||
config: &VictoryConfig,
|
||||
turn: u32,
|
||||
) -> Option<(u8, VictoryType)> {
|
||||
// Domination: a player controls all other players' original capitals.
|
||||
if config.domination_requires_all_capitals && players.len() > 1 {
|
||||
// Skip the entire branch if a min-turn floor is set and we haven't
|
||||
// reached it yet (warcouncil p1-29 H2). Other victory types still fire.
|
||||
if config.domination_requires_all_capitals && players.len() > 1 && turn >= config.min_domination_turn {
|
||||
let capitals: Vec<Option<(i32, i32)>> = players
|
||||
.iter()
|
||||
.map(|p| p.capital_position)
|
||||
|
|
@ -341,6 +366,7 @@ mod tests {
|
|||
science_techs_required: vec![],
|
||||
science_cost_base: 500,
|
||||
domination_requires_all_capitals: false,
|
||||
min_domination_turn: 0,
|
||||
};
|
||||
let mut p0 = bare_player(0);
|
||||
let p1 = bare_player(1);
|
||||
|
|
@ -369,6 +395,7 @@ mod tests {
|
|||
culture_threshold: i64::MAX,
|
||||
city_count_threshold: usize::MAX,
|
||||
domination_requires_all_capitals: false,
|
||||
min_domination_turn: 0,
|
||||
};
|
||||
let mut p0 = bare_player(0);
|
||||
let p1 = bare_player(1);
|
||||
|
|
@ -403,6 +430,7 @@ mod tests {
|
|||
science_techs_required: vec![],
|
||||
science_cost_base: 500,
|
||||
domination_requires_all_capitals: false,
|
||||
min_domination_turn: 0,
|
||||
};
|
||||
let mut p0 = bare_player(0);
|
||||
let p1 = bare_player(1);
|
||||
|
|
@ -421,6 +449,7 @@ mod tests {
|
|||
fn domination_requires_controlling_all_enemy_capitals() {
|
||||
let config = VictoryConfig {
|
||||
domination_requires_all_capitals: true,
|
||||
min_domination_turn: 0,
|
||||
gold_threshold: i64::MAX,
|
||||
culture_threshold: i64::MAX,
|
||||
city_count_threshold: usize::MAX,
|
||||
|
|
@ -586,6 +615,7 @@ mod tests {
|
|||
// domination wins because it's checked first.
|
||||
let config = VictoryConfig {
|
||||
domination_requires_all_capitals: true,
|
||||
min_domination_turn: 0,
|
||||
gold_threshold: 100,
|
||||
culture_threshold: i64::MAX,
|
||||
city_count_threshold: usize::MAX,
|
||||
|
|
@ -620,6 +650,7 @@ mod tests {
|
|||
// before Economic → p1 should win.
|
||||
let config = VictoryConfig {
|
||||
domination_requires_all_capitals: false, // disable domination path
|
||||
min_domination_turn: 0,
|
||||
gold_threshold: 100,
|
||||
culture_threshold: i64::MAX,
|
||||
city_count_threshold: usize::MAX,
|
||||
|
|
@ -650,6 +681,7 @@ mod tests {
|
|||
fn priority_order_economic_beats_culture_across_players() {
|
||||
let config = VictoryConfig {
|
||||
domination_requires_all_capitals: false,
|
||||
min_domination_turn: 0,
|
||||
gold_threshold: 100,
|
||||
culture_threshold: 100,
|
||||
city_count_threshold: usize::MAX,
|
||||
|
|
@ -675,6 +707,7 @@ mod tests {
|
|||
fn priority_order_culture_beats_city_count_across_players() {
|
||||
let config = VictoryConfig {
|
||||
domination_requires_all_capitals: false,
|
||||
min_domination_turn: 0,
|
||||
gold_threshold: i64::MAX,
|
||||
culture_threshold: 100,
|
||||
city_count_threshold: 1,
|
||||
|
|
@ -701,6 +734,7 @@ mod tests {
|
|||
fn same_condition_ties_break_to_lower_index() {
|
||||
let config = VictoryConfig {
|
||||
domination_requires_all_capitals: false,
|
||||
min_domination_turn: 0,
|
||||
gold_threshold: 1000,
|
||||
culture_threshold: i64::MAX,
|
||||
city_count_threshold: usize::MAX,
|
||||
|
|
@ -727,6 +761,7 @@ mod tests {
|
|||
fn eliminated_player_skipped_in_domination() {
|
||||
let config = VictoryConfig {
|
||||
domination_requires_all_capitals: true,
|
||||
min_domination_turn: 0,
|
||||
gold_threshold: i64::MAX,
|
||||
culture_threshold: i64::MAX,
|
||||
city_count_threshold: usize::MAX,
|
||||
|
|
@ -763,6 +798,7 @@ mod tests {
|
|||
fn eliminated_player_cannot_win_domination() {
|
||||
let config = VictoryConfig {
|
||||
domination_requires_all_capitals: true,
|
||||
min_domination_turn: 0,
|
||||
gold_threshold: i64::MAX,
|
||||
culture_threshold: i64::MAX,
|
||||
city_count_threshold: usize::MAX,
|
||||
|
|
@ -800,6 +836,7 @@ mod tests {
|
|||
fn empty_science_techs_required_does_not_auto_grant() {
|
||||
let config = VictoryConfig {
|
||||
domination_requires_all_capitals: false,
|
||||
min_domination_turn: 0,
|
||||
gold_threshold: i64::MAX,
|
||||
culture_threshold: i64::MAX,
|
||||
city_count_threshold: usize::MAX,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue