Rehost annual deep-distress bankruptcy fallback
This commit is contained in:
parent
64fc4a8488
commit
5e19e8985a
6 changed files with 354 additions and 16 deletions
|
|
@ -92,7 +92,10 @@ another isolated finance leaf. A checked-in
|
||||||
fixed-world finance-policy seam now also carries the raw stock, bond, bankruptcy, and dividend
|
fixed-world finance-policy seam now also carries the raw stock, bond, bankruptcy, and dividend
|
||||||
policy bytes from the `0x32c8` save block, and the first annual creditor-pressure branch now runs
|
policy bytes from the `0x32c8` save block, and the first annual creditor-pressure branch now runs
|
||||||
headlessly as a pure runtime reader over owned annual-finance state, support-adjusted share price,
|
headlessly as a pure runtime reader over owned annual-finance state, support-adjusted share price,
|
||||||
and current world finance policy rather than as a notes-only atlas fragment. The working rule on the remaining frontier is explicit now too: when a lane is still ambiguous, we
|
and current world finance policy rather than as a notes-only atlas fragment. The later deep-
|
||||||
|
distress bankruptcy fallback is now rehosted on that same owner surface too, using the save-native
|
||||||
|
cash reader seam plus the first three trailing net-profit years instead of another ad hoc probe.
|
||||||
|
The working rule on the remaining frontier is explicit now too: when a lane is still ambiguous, we
|
||||||
should prefer rehosting the owning source state or the real reader/setter family rather than
|
should prefer rehosting the owning source state or the real reader/setter family rather than
|
||||||
guessing one more derived leaf field from nearby offsets. A checked-in
|
guessing one more derived leaf field from nearby offsets. A checked-in
|
||||||
`EventEffects` export now exists too in
|
`EventEffects` export now exists too in
|
||||||
|
|
|
||||||
|
|
@ -52,13 +52,13 @@ pub use runtime::{
|
||||||
RUNTIME_WORLD_ISSUE_PRIME_RATE, RuntimeCargoCatalogEntry, RuntimeCargoClass,
|
RUNTIME_WORLD_ISSUE_PRIME_RATE, RuntimeCargoCatalogEntry, RuntimeCargoClass,
|
||||||
RuntimeCargoPriceTarget, RuntimeCargoProductionTarget, RuntimeChairmanMetric,
|
RuntimeCargoPriceTarget, RuntimeCargoProductionTarget, RuntimeChairmanMetric,
|
||||||
RuntimeChairmanProfile, RuntimeChairmanTarget, RuntimeCompany,
|
RuntimeChairmanProfile, RuntimeChairmanTarget, RuntimeCompany,
|
||||||
RuntimeCompanyAnnualCreditorPressureState, RuntimeCompanyAnnualFinanceState,
|
RuntimeCompanyAnnualCreditorPressureState, RuntimeCompanyAnnualDeepDistressState,
|
||||||
RuntimeCompanyBondSlot, RuntimeCompanyConditionTestScope, RuntimeCompanyControllerKind,
|
RuntimeCompanyAnnualFinanceState, RuntimeCompanyBondSlot, RuntimeCompanyConditionTestScope,
|
||||||
RuntimeCompanyMarketMetric, RuntimeCompanyMarketState, RuntimeCompanyMetric,
|
RuntimeCompanyControllerKind, RuntimeCompanyMarketMetric, RuntimeCompanyMarketState,
|
||||||
RuntimeCompanyStatBandCandidate, RuntimeCompanyStatSelector, RuntimeCompanyTarget,
|
RuntimeCompanyMetric, RuntimeCompanyStatBandCandidate, RuntimeCompanyStatSelector,
|
||||||
RuntimeCompanyTerritoryAccess, RuntimeCompanyTerritoryTrackPieceCount, RuntimeCondition,
|
RuntimeCompanyTarget, RuntimeCompanyTerritoryAccess, RuntimeCompanyTerritoryTrackPieceCount,
|
||||||
RuntimeConditionComparator, RuntimeEffect, RuntimeEventRecord, RuntimeEventRecordTemplate,
|
RuntimeCondition, RuntimeConditionComparator, RuntimeEffect, RuntimeEventRecord,
|
||||||
RuntimeLocomotiveCatalogEntry, RuntimePackedEventCollectionSummary,
|
RuntimeEventRecordTemplate, RuntimeLocomotiveCatalogEntry, RuntimePackedEventCollectionSummary,
|
||||||
RuntimePackedEventCompactControlSummary, RuntimePackedEventConditionRowSummary,
|
RuntimePackedEventCompactControlSummary, RuntimePackedEventConditionRowSummary,
|
||||||
RuntimePackedEventGroupedEffectRowSummary, RuntimePackedEventNegativeSentinelScopeSummary,
|
RuntimePackedEventGroupedEffectRowSummary, RuntimePackedEventNegativeSentinelScopeSummary,
|
||||||
RuntimePackedEventRecordSummary, RuntimePackedEventTextBandSummary, RuntimePlayer,
|
RuntimePackedEventRecordSummary, RuntimePackedEventTextBandSummary, RuntimePlayer,
|
||||||
|
|
@ -66,11 +66,11 @@ pub use runtime::{
|
||||||
RuntimeServiceState, RuntimeState, RuntimeTerritory, RuntimeTerritoryMetric,
|
RuntimeServiceState, RuntimeState, RuntimeTerritory, RuntimeTerritoryMetric,
|
||||||
RuntimeTerritoryTarget, RuntimeTrackMetric, RuntimeTrackPieceCounts, RuntimeTrain,
|
RuntimeTerritoryTarget, RuntimeTrackMetric, RuntimeTrackPieceCounts, RuntimeTrain,
|
||||||
RuntimeWorldFinanceNeighborhoodCandidate, RuntimeWorldIssueState, RuntimeWorldRestoreState,
|
RuntimeWorldFinanceNeighborhoodCandidate, RuntimeWorldIssueState, RuntimeWorldRestoreState,
|
||||||
runtime_company_annual_creditor_pressure_state, runtime_company_annual_finance_state,
|
runtime_company_annual_creditor_pressure_state, runtime_company_annual_deep_distress_state,
|
||||||
runtime_company_assigned_share_pool, runtime_company_average_live_bond_coupon,
|
runtime_company_annual_finance_state, runtime_company_assigned_share_pool,
|
||||||
runtime_company_book_value_per_share, runtime_company_credit_rating,
|
runtime_company_average_live_bond_coupon, runtime_company_book_value_per_share,
|
||||||
runtime_company_investor_confidence, runtime_company_management_attitude,
|
runtime_company_credit_rating, runtime_company_investor_confidence,
|
||||||
runtime_company_market_value, runtime_company_prime_rate,
|
runtime_company_management_attitude, runtime_company_market_value, runtime_company_prime_rate,
|
||||||
runtime_company_recent_per_share_subscore, runtime_company_stat_value,
|
runtime_company_recent_per_share_subscore, runtime_company_stat_value,
|
||||||
runtime_company_stat_value_f64, runtime_company_unassigned_share_pool,
|
runtime_company_stat_value_f64, runtime_company_unassigned_share_pool,
|
||||||
runtime_world_annual_finance_mode_active, runtime_world_bankruptcy_allowed,
|
runtime_world_annual_finance_mode_active, runtime_world_bankruptcy_allowed,
|
||||||
|
|
|
||||||
|
|
@ -208,6 +208,25 @@ pub struct RuntimeCompanyAnnualCreditorPressureState {
|
||||||
pub eligible_for_bankruptcy_branch: bool,
|
pub eligible_for_bankruptcy_branch: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub struct RuntimeCompanyAnnualDeepDistressState {
|
||||||
|
pub company_id: u32,
|
||||||
|
#[serde(default)]
|
||||||
|
pub bankruptcy_allowed: Option<bool>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub years_since_founding: Option<u32>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub years_since_last_bankruptcy: Option<u32>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub current_cash: Option<i64>,
|
||||||
|
pub recent_first_three_net_profit_years: Vec<i64>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub deep_distress_cash_floor: Option<i64>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub deep_distress_net_profit_floor: Option<i64>,
|
||||||
|
pub eligible_for_bankruptcy_fallback: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
|
||||||
pub struct RuntimeTrackPieceCounts {
|
pub struct RuntimeTrackPieceCounts {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
|
@ -2894,6 +2913,52 @@ pub fn runtime_company_annual_creditor_pressure_state(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn runtime_company_annual_deep_distress_state(
|
||||||
|
state: &RuntimeState,
|
||||||
|
company_id: u32,
|
||||||
|
) -> Option<RuntimeCompanyAnnualDeepDistressState> {
|
||||||
|
let annual_finance_state = runtime_company_annual_finance_state(state, company_id)?;
|
||||||
|
let current_cash = runtime_company_control_transfer_stat_value_f64(
|
||||||
|
state,
|
||||||
|
company_id,
|
||||||
|
RUNTIME_COMPANY_STAT_SLOT_CURRENT_CASH,
|
||||||
|
)
|
||||||
|
.and_then(runtime_round_f64_to_i64);
|
||||||
|
let recent_first_three_net_profit_years = annual_finance_state
|
||||||
|
.trailing_full_year_net_profits
|
||||||
|
.iter()
|
||||||
|
.take(3)
|
||||||
|
.copied()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let deep_distress_cash_floor = Some(-300_000);
|
||||||
|
let deep_distress_net_profit_floor = Some(-20_000);
|
||||||
|
let eligible_for_bankruptcy_fallback = runtime_world_bankruptcy_allowed(state) == Some(true)
|
||||||
|
&& current_cash
|
||||||
|
.zip(deep_distress_cash_floor)
|
||||||
|
.is_some_and(|(value, floor)| value <= floor)
|
||||||
|
&& annual_finance_state
|
||||||
|
.years_since_founding
|
||||||
|
.is_some_and(|years| years >= 3)
|
||||||
|
&& recent_first_three_net_profit_years.len() == 3
|
||||||
|
&& recent_first_three_net_profit_years
|
||||||
|
.iter()
|
||||||
|
.all(|value| *value <= deep_distress_net_profit_floor.unwrap())
|
||||||
|
&& annual_finance_state
|
||||||
|
.years_since_last_bankruptcy
|
||||||
|
.is_some_and(|years| years >= 5);
|
||||||
|
Some(RuntimeCompanyAnnualDeepDistressState {
|
||||||
|
company_id,
|
||||||
|
bankruptcy_allowed: runtime_world_bankruptcy_allowed(state),
|
||||||
|
years_since_founding: annual_finance_state.years_since_founding,
|
||||||
|
years_since_last_bankruptcy: annual_finance_state.years_since_last_bankruptcy,
|
||||||
|
current_cash,
|
||||||
|
recent_first_three_net_profit_years,
|
||||||
|
deep_distress_cash_floor,
|
||||||
|
deep_distress_net_profit_floor,
|
||||||
|
eligible_for_bankruptcy_fallback,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn runtime_world_absolute_counter(state: &RuntimeState) -> Option<u32> {
|
pub fn runtime_world_absolute_counter(state: &RuntimeState) -> Option<u32> {
|
||||||
state.world_restore.absolute_counter_raw_u32
|
state.world_restore.absolute_counter_raw_u32
|
||||||
}
|
}
|
||||||
|
|
@ -6936,6 +7001,118 @@ mod tests {
|
||||||
assert!(pressure_state.eligible_for_bankruptcy_branch);
|
assert!(pressure_state.eligible_for_bankruptcy_branch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn derives_annual_deep_distress_bankruptcy_fallback_from_rehosted_finance_owner_state() {
|
||||||
|
let mut year_stat_family_qword_bits = vec![
|
||||||
|
0u64;
|
||||||
|
((RUNTIME_COMPANY_STAT_SLOT_COUNT + 2) * RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN)
|
||||||
|
as usize
|
||||||
|
];
|
||||||
|
let write_current_value = |bits: &mut Vec<u64>, slot_id: u32, value: f64| {
|
||||||
|
let index = (slot_id * RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN) as usize;
|
||||||
|
bits[index] = value.to_bits();
|
||||||
|
};
|
||||||
|
let write_prior_year_value =
|
||||||
|
|bits: &mut Vec<u64>, slot_id: u32, year_delta: u32, value: f64| {
|
||||||
|
let index = (slot_id * RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN + year_delta) as usize;
|
||||||
|
bits[index] = value.to_bits();
|
||||||
|
};
|
||||||
|
write_current_value(&mut year_stat_family_qword_bits, 0x0d, -350_000.0);
|
||||||
|
write_prior_year_value(&mut year_stat_family_qword_bits, 0x01, 1, 10_000.0);
|
||||||
|
write_prior_year_value(&mut year_stat_family_qword_bits, 0x01, 2, 15_000.0);
|
||||||
|
write_prior_year_value(&mut year_stat_family_qword_bits, 0x01, 3, 12_000.0);
|
||||||
|
write_prior_year_value(&mut year_stat_family_qword_bits, 0x09, 1, -35_000.0);
|
||||||
|
write_prior_year_value(&mut year_stat_family_qword_bits, 0x09, 2, -38_000.0);
|
||||||
|
write_prior_year_value(&mut year_stat_family_qword_bits, 0x09, 3, -33_000.0);
|
||||||
|
|
||||||
|
let state = RuntimeState {
|
||||||
|
calendar: CalendarPoint {
|
||||||
|
year: 1845,
|
||||||
|
month_slot: 0,
|
||||||
|
phase_slot: 0,
|
||||||
|
tick_slot: 0,
|
||||||
|
},
|
||||||
|
world_flags: BTreeMap::new(),
|
||||||
|
save_profile: RuntimeSaveProfileState::default(),
|
||||||
|
world_restore: RuntimeWorldRestoreState {
|
||||||
|
packed_year_word_raw_u16: Some(1845),
|
||||||
|
bankruptcy_policy_raw_u8: Some(0),
|
||||||
|
bankruptcy_allowed: Some(true),
|
||||||
|
..RuntimeWorldRestoreState::default()
|
||||||
|
},
|
||||||
|
metadata: BTreeMap::new(),
|
||||||
|
companies: vec![RuntimeCompany {
|
||||||
|
company_id: 9,
|
||||||
|
current_cash: 0,
|
||||||
|
debt: 0,
|
||||||
|
credit_rating_score: None,
|
||||||
|
prime_rate: None,
|
||||||
|
active: true,
|
||||||
|
available_track_laying_capacity: None,
|
||||||
|
controller_kind: RuntimeCompanyControllerKind::Unknown,
|
||||||
|
linked_chairman_profile_id: None,
|
||||||
|
book_value_per_share: 0,
|
||||||
|
investor_confidence: 0,
|
||||||
|
management_attitude: 0,
|
||||||
|
takeover_cooldown_year: None,
|
||||||
|
merger_cooldown_year: None,
|
||||||
|
track_piece_counts: RuntimeTrackPieceCounts::default(),
|
||||||
|
}],
|
||||||
|
selected_company_id: None,
|
||||||
|
players: Vec::new(),
|
||||||
|
selected_player_id: None,
|
||||||
|
chairman_profiles: Vec::new(),
|
||||||
|
selected_chairman_profile_id: None,
|
||||||
|
trains: Vec::new(),
|
||||||
|
locomotive_catalog: Vec::new(),
|
||||||
|
cargo_catalog: Vec::new(),
|
||||||
|
territories: Vec::new(),
|
||||||
|
company_territory_track_piece_counts: Vec::new(),
|
||||||
|
company_territory_access: Vec::new(),
|
||||||
|
packed_event_collection: None,
|
||||||
|
event_runtime_records: Vec::new(),
|
||||||
|
candidate_availability: BTreeMap::new(),
|
||||||
|
named_locomotive_availability: BTreeMap::new(),
|
||||||
|
named_locomotive_cost: BTreeMap::new(),
|
||||||
|
all_cargo_price_override: None,
|
||||||
|
named_cargo_price_overrides: BTreeMap::new(),
|
||||||
|
all_cargo_production_override: None,
|
||||||
|
factory_cargo_production_override: None,
|
||||||
|
farm_mine_cargo_production_override: None,
|
||||||
|
named_cargo_production_overrides: BTreeMap::new(),
|
||||||
|
cargo_production_overrides: BTreeMap::new(),
|
||||||
|
world_runtime_variables: BTreeMap::new(),
|
||||||
|
company_runtime_variables: BTreeMap::new(),
|
||||||
|
player_runtime_variables: BTreeMap::new(),
|
||||||
|
territory_runtime_variables: BTreeMap::new(),
|
||||||
|
world_scalar_overrides: BTreeMap::new(),
|
||||||
|
special_conditions: BTreeMap::new(),
|
||||||
|
service_state: RuntimeServiceState {
|
||||||
|
company_market_state: BTreeMap::from([(
|
||||||
|
9,
|
||||||
|
RuntimeCompanyMarketState {
|
||||||
|
founding_year: 1841,
|
||||||
|
last_bankruptcy_year: 1840,
|
||||||
|
year_stat_family_qword_bits,
|
||||||
|
..RuntimeCompanyMarketState::default()
|
||||||
|
},
|
||||||
|
)]),
|
||||||
|
..RuntimeServiceState::default()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let pressure_state =
|
||||||
|
runtime_company_annual_deep_distress_state(&state, 9).expect("deep distress state");
|
||||||
|
assert_eq!(pressure_state.current_cash, Some(-350_000));
|
||||||
|
assert_eq!(
|
||||||
|
pressure_state.recent_first_three_net_profit_years,
|
||||||
|
vec![-25_000, -23_000, -21_000]
|
||||||
|
);
|
||||||
|
assert_eq!(pressure_state.deep_distress_cash_floor, Some(-300_000));
|
||||||
|
assert_eq!(pressure_state.deep_distress_net_profit_floor, Some(-20_000));
|
||||||
|
assert!(pressure_state.eligible_for_bankruptcy_fallback);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn reads_company_market_metrics_from_annual_finance_reader() {
|
fn reads_company_market_metrics_from_annual_finance_reader() {
|
||||||
let current_issue_calendar_word = 0x0101_0726;
|
let current_issue_calendar_word = 0x0101_0726;
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@ use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
CalendarPoint, RuntimeState, runtime_company_annual_creditor_pressure_state,
|
CalendarPoint, RuntimeState, runtime_company_annual_creditor_pressure_state,
|
||||||
runtime_company_annual_finance_state, runtime_company_unassigned_share_pool,
|
runtime_company_annual_deep_distress_state, runtime_company_annual_finance_state,
|
||||||
|
runtime_company_unassigned_share_pool,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn raw_u32_to_f32_text(raw: u32) -> String {
|
fn raw_u32_to_f32_text(raw: u32) -> String {
|
||||||
|
|
@ -105,6 +106,11 @@ pub struct RuntimeSummary {
|
||||||
pub selected_company_creditor_pressure_current_fuel_cost: Option<i64>,
|
pub selected_company_creditor_pressure_current_fuel_cost: Option<i64>,
|
||||||
pub selected_company_creditor_pressure_current_fuel_cost_floor: Option<i64>,
|
pub selected_company_creditor_pressure_current_fuel_cost_floor: Option<i64>,
|
||||||
pub selected_company_creditor_pressure_eligible_for_bankruptcy_branch: Option<bool>,
|
pub selected_company_creditor_pressure_eligible_for_bankruptcy_branch: Option<bool>,
|
||||||
|
pub selected_company_deep_distress_current_cash: Option<i64>,
|
||||||
|
pub selected_company_deep_distress_recent_first_three_net_profit_years: Vec<i64>,
|
||||||
|
pub selected_company_deep_distress_cash_floor: Option<i64>,
|
||||||
|
pub selected_company_deep_distress_net_profit_floor: Option<i64>,
|
||||||
|
pub selected_company_deep_distress_eligible_for_bankruptcy_fallback: Option<bool>,
|
||||||
pub player_count: usize,
|
pub player_count: usize,
|
||||||
pub chairman_profile_count: usize,
|
pub chairman_profile_count: usize,
|
||||||
pub active_chairman_profile_count: usize,
|
pub active_chairman_profile_count: usize,
|
||||||
|
|
@ -198,6 +204,9 @@ impl RuntimeSummary {
|
||||||
state.selected_company_id.and_then(|company_id| {
|
state.selected_company_id.and_then(|company_id| {
|
||||||
runtime_company_annual_creditor_pressure_state(state, company_id)
|
runtime_company_annual_creditor_pressure_state(state, company_id)
|
||||||
});
|
});
|
||||||
|
let selected_company_deep_distress_state = state
|
||||||
|
.selected_company_id
|
||||||
|
.and_then(|company_id| runtime_company_annual_deep_distress_state(state, company_id));
|
||||||
Self {
|
Self {
|
||||||
calendar: state.calendar,
|
calendar: state.calendar,
|
||||||
calendar_projection_source: state.metadata.get("save_slice.calendar_source").cloned(),
|
calendar_projection_source: state.metadata.get("save_slice.calendar_source").cloned(),
|
||||||
|
|
@ -451,6 +460,26 @@ impl RuntimeSummary {
|
||||||
selected_company_creditor_pressure_state
|
selected_company_creditor_pressure_state
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|pressure_state| pressure_state.eligible_for_bankruptcy_branch),
|
.map(|pressure_state| pressure_state.eligible_for_bankruptcy_branch),
|
||||||
|
selected_company_deep_distress_current_cash: selected_company_deep_distress_state
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|pressure_state| pressure_state.current_cash),
|
||||||
|
selected_company_deep_distress_recent_first_three_net_profit_years:
|
||||||
|
selected_company_deep_distress_state
|
||||||
|
.as_ref()
|
||||||
|
.map(|pressure_state| {
|
||||||
|
pressure_state.recent_first_three_net_profit_years.clone()
|
||||||
|
})
|
||||||
|
.unwrap_or_default(),
|
||||||
|
selected_company_deep_distress_cash_floor: selected_company_deep_distress_state
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|pressure_state| pressure_state.deep_distress_cash_floor),
|
||||||
|
selected_company_deep_distress_net_profit_floor: selected_company_deep_distress_state
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|pressure_state| pressure_state.deep_distress_net_profit_floor),
|
||||||
|
selected_company_deep_distress_eligible_for_bankruptcy_fallback:
|
||||||
|
selected_company_deep_distress_state
|
||||||
|
.as_ref()
|
||||||
|
.map(|pressure_state| pressure_state.eligible_for_bankruptcy_fallback),
|
||||||
player_count: state.players.len(),
|
player_count: state.players.len(),
|
||||||
chairman_profile_count: state.chairman_profiles.len(),
|
chairman_profile_count: state.chairman_profiles.len(),
|
||||||
active_chairman_profile_count: state
|
active_chairman_profile_count: state
|
||||||
|
|
@ -2436,4 +2465,129 @@ mod tests {
|
||||||
Some(true)
|
Some(true)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn summarizes_selected_company_deep_distress_fallback_state() {
|
||||||
|
let mut year_stat_family_qword_bits = vec![
|
||||||
|
0u64;
|
||||||
|
((crate::RUNTIME_COMPANY_STAT_SLOT_COUNT + 2)
|
||||||
|
* crate::RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN)
|
||||||
|
as usize
|
||||||
|
];
|
||||||
|
let write_current_value = |bits: &mut Vec<u64>, slot_id: u32, value: f64| {
|
||||||
|
let index = (slot_id * crate::RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN) as usize;
|
||||||
|
bits[index] = value.to_bits();
|
||||||
|
};
|
||||||
|
let write_prior_year_value =
|
||||||
|
|bits: &mut Vec<u64>, slot_id: u32, year_delta: u32, value: f64| {
|
||||||
|
let index =
|
||||||
|
(slot_id * crate::RUNTIME_COMPANY_YEAR_STAT_FAMILY_SPAN + year_delta) as usize;
|
||||||
|
bits[index] = value.to_bits();
|
||||||
|
};
|
||||||
|
write_current_value(&mut year_stat_family_qword_bits, 0x0d, -350_000.0);
|
||||||
|
write_prior_year_value(&mut year_stat_family_qword_bits, 0x01, 1, 10_000.0);
|
||||||
|
write_prior_year_value(&mut year_stat_family_qword_bits, 0x01, 2, 15_000.0);
|
||||||
|
write_prior_year_value(&mut year_stat_family_qword_bits, 0x01, 3, 12_000.0);
|
||||||
|
write_prior_year_value(&mut year_stat_family_qword_bits, 0x09, 1, -35_000.0);
|
||||||
|
write_prior_year_value(&mut year_stat_family_qword_bits, 0x09, 2, -38_000.0);
|
||||||
|
write_prior_year_value(&mut year_stat_family_qword_bits, 0x09, 3, -33_000.0);
|
||||||
|
|
||||||
|
let state = RuntimeState {
|
||||||
|
calendar: CalendarPoint {
|
||||||
|
year: 1845,
|
||||||
|
month_slot: 0,
|
||||||
|
phase_slot: 0,
|
||||||
|
tick_slot: 0,
|
||||||
|
},
|
||||||
|
world_flags: BTreeMap::new(),
|
||||||
|
save_profile: RuntimeSaveProfileState::default(),
|
||||||
|
world_restore: RuntimeWorldRestoreState {
|
||||||
|
packed_year_word_raw_u16: Some(1845),
|
||||||
|
bankruptcy_policy_raw_u8: Some(0),
|
||||||
|
bankruptcy_allowed: Some(true),
|
||||||
|
..RuntimeWorldRestoreState::default()
|
||||||
|
},
|
||||||
|
metadata: BTreeMap::new(),
|
||||||
|
companies: vec![RuntimeCompany {
|
||||||
|
company_id: 9,
|
||||||
|
current_cash: 0,
|
||||||
|
debt: 0,
|
||||||
|
credit_rating_score: None,
|
||||||
|
prime_rate: None,
|
||||||
|
active: true,
|
||||||
|
available_track_laying_capacity: None,
|
||||||
|
controller_kind: crate::RuntimeCompanyControllerKind::Unknown,
|
||||||
|
linked_chairman_profile_id: None,
|
||||||
|
book_value_per_share: 0,
|
||||||
|
investor_confidence: 0,
|
||||||
|
management_attitude: 0,
|
||||||
|
takeover_cooldown_year: None,
|
||||||
|
merger_cooldown_year: None,
|
||||||
|
track_piece_counts: RuntimeTrackPieceCounts::default(),
|
||||||
|
}],
|
||||||
|
selected_company_id: Some(9),
|
||||||
|
players: Vec::new(),
|
||||||
|
selected_player_id: None,
|
||||||
|
chairman_profiles: Vec::new(),
|
||||||
|
selected_chairman_profile_id: None,
|
||||||
|
trains: Vec::new(),
|
||||||
|
locomotive_catalog: Vec::new(),
|
||||||
|
cargo_catalog: Vec::new(),
|
||||||
|
territories: Vec::new(),
|
||||||
|
company_territory_track_piece_counts: Vec::new(),
|
||||||
|
company_territory_access: Vec::new(),
|
||||||
|
packed_event_collection: None,
|
||||||
|
event_runtime_records: Vec::new(),
|
||||||
|
candidate_availability: BTreeMap::new(),
|
||||||
|
named_locomotive_availability: BTreeMap::new(),
|
||||||
|
named_locomotive_cost: BTreeMap::new(),
|
||||||
|
all_cargo_price_override: None,
|
||||||
|
named_cargo_price_overrides: BTreeMap::new(),
|
||||||
|
all_cargo_production_override: None,
|
||||||
|
factory_cargo_production_override: None,
|
||||||
|
farm_mine_cargo_production_override: None,
|
||||||
|
named_cargo_production_overrides: BTreeMap::new(),
|
||||||
|
cargo_production_overrides: BTreeMap::new(),
|
||||||
|
world_runtime_variables: BTreeMap::new(),
|
||||||
|
company_runtime_variables: BTreeMap::new(),
|
||||||
|
player_runtime_variables: BTreeMap::new(),
|
||||||
|
territory_runtime_variables: BTreeMap::new(),
|
||||||
|
world_scalar_overrides: BTreeMap::new(),
|
||||||
|
special_conditions: BTreeMap::new(),
|
||||||
|
service_state: RuntimeServiceState {
|
||||||
|
company_market_state: BTreeMap::from([(
|
||||||
|
9,
|
||||||
|
crate::RuntimeCompanyMarketState {
|
||||||
|
founding_year: 1841,
|
||||||
|
last_bankruptcy_year: 1840,
|
||||||
|
year_stat_family_qword_bits,
|
||||||
|
..crate::RuntimeCompanyMarketState::default()
|
||||||
|
},
|
||||||
|
)]),
|
||||||
|
..RuntimeServiceState::default()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let summary = RuntimeSummary::from_state(&state);
|
||||||
|
assert_eq!(
|
||||||
|
summary.selected_company_deep_distress_current_cash,
|
||||||
|
Some(-350_000)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
summary.selected_company_deep_distress_recent_first_three_net_profit_years,
|
||||||
|
vec![-25_000, -23_000, -21_000]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
summary.selected_company_deep_distress_cash_floor,
|
||||||
|
Some(-300_000)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
summary.selected_company_deep_distress_net_profit_floor,
|
||||||
|
Some(-20_000)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
summary.selected_company_deep_distress_eligible_for_bankruptcy_fallback,
|
||||||
|
Some(true)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -136,7 +136,9 @@ The highest-value next passes are now:
|
||||||
the largest live bond principal and the chosen highest-coupon live bond principal into that same
|
the largest live bond principal and the chosen highest-coupon live bond principal into that same
|
||||||
owner-state surface; the same fixed-world save block now also carries the raw stock, bond,
|
owner-state surface; the same fixed-world save block now also carries the raw stock, bond,
|
||||||
bankruptcy, and dividend finance-policy bytes, and the first annual creditor-pressure branch now
|
bankruptcy, and dividend finance-policy bytes, and the first annual creditor-pressure branch now
|
||||||
executes as a pure runtime reader over that owner state instead of remaining atlas-only
|
executes as a pure runtime reader over that owner state instead of remaining atlas-only; the
|
||||||
|
later deep-distress bankruptcy fallback now runs on that same save-native cash and trailing-
|
||||||
|
net-profit surface too
|
||||||
- the project rule on the remaining closure work is now explicit too: when one runtime-facing field
|
- the project rule on the remaining closure work is now explicit too: when one runtime-facing field
|
||||||
is still ambiguous, prefer rehosting the owning source state or real reader/setter family first
|
is still ambiguous, prefer rehosting the owning source state or real reader/setter family first
|
||||||
instead of guessing another derived leaf field from neighboring raw offsets
|
instead of guessing another derived leaf field from neighboring raw offsets
|
||||||
|
|
|
||||||
|
|
@ -228,7 +228,9 @@ annual-finance state, so later stock-capital gates can extend a rehosted owner-s
|
||||||
of guessing another finance leaf. The same fixed-world save block now also carries the raw stock,
|
of guessing another finance leaf. The same fixed-world save block now also carries the raw stock,
|
||||||
bond, bankruptcy, and dividend finance-policy bytes, and the earliest annual creditor-pressure
|
bond, bankruptcy, and dividend finance-policy bytes, and the earliest annual creditor-pressure
|
||||||
bankruptcy branch now runs as a pure runtime reader over owned annual-finance state, support-
|
bankruptcy branch now runs as a pure runtime reader over owned annual-finance state, support-
|
||||||
adjusted share price, and those policy bytes rather than staying in atlas prose only.
|
adjusted share price, and those policy bytes rather than staying in atlas prose only. The later
|
||||||
|
deep-distress bankruptcy fallback now rides the same owner-state seam too, using the save-native
|
||||||
|
cash reader plus the first three trailing net-profit years instead of a parallel raw-offset guess.
|
||||||
|
|
||||||
## Why This Boundary
|
## Why This Boundary
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue