Rehost bankruptcy debt-halving finance path
This commit is contained in:
parent
ec734c5d92
commit
ad048f1528
4 changed files with 85 additions and 33 deletions
|
|
@ -109,6 +109,8 @@ the shellless creditor-pressure-bankruptcy, deep-distress-bankruptcy, dividend-a
|
|||
stock-repurchase, stock-issue, and bond-issue branches by mutating owned company activity,
|
||||
dividend, company stat-post, outstanding-share, issue-calendar, and live bond-slot state instead
|
||||
of stopping at reader-only diagnostics.
|
||||
Those bankruptcy branches now follow the grounded owner semantics too: they stamp the bankruptcy
|
||||
year and halve live bond principals in place instead of treating bankruptcy as a liquidation path.
|
||||
The same save-native live bond-slot surface now also carries per-slot maturity years all the way
|
||||
through runtime summaries and annual bond policy state, which is the next owner seam needed for
|
||||
shellless repayment and bond-burden simulation instead of another round of raw-slot guessing.
|
||||
|
|
|
|||
|
|
@ -317,31 +317,53 @@ fn service_apply_company_bankruptcy(state: &mut RuntimeState, company_id: u32) -
|
|||
};
|
||||
|
||||
let mut company_mutated = false;
|
||||
if let Some(market_state) = state.service_state.company_market_state.get_mut(&company_id) {
|
||||
market_state.last_bankruptcy_year = bankruptcy_year;
|
||||
for slot in &mut market_state.live_bond_slots {
|
||||
slot.principal /= 2;
|
||||
}
|
||||
market_state.live_bond_slots.retain(|slot| slot.principal > 0);
|
||||
market_state.bond_count = market_state.live_bond_slots.len().min(u8::MAX as usize) as u8;
|
||||
market_state.largest_live_bond_principal =
|
||||
market_state.live_bond_slots.iter().map(|slot| slot.principal).max();
|
||||
market_state.highest_coupon_live_bond_principal = market_state
|
||||
.live_bond_slots
|
||||
.iter()
|
||||
.filter_map(|slot| {
|
||||
let coupon = f32::from_bits(slot.coupon_rate_raw_u32) as f64;
|
||||
coupon.is_finite().then_some((coupon, slot.principal))
|
||||
})
|
||||
.max_by(|left, right| {
|
||||
left.0
|
||||
.partial_cmp(&right.0)
|
||||
.unwrap_or(std::cmp::Ordering::Equal)
|
||||
})
|
||||
.map(|(_, principal)| principal);
|
||||
company_mutated = true;
|
||||
}
|
||||
|
||||
let remaining_debt = state
|
||||
.service_state
|
||||
.company_market_state
|
||||
.get(&company_id)
|
||||
.map(|market_state| {
|
||||
market_state
|
||||
.live_bond_slots
|
||||
.iter()
|
||||
.map(|slot| u64::from(slot.principal))
|
||||
.sum::<u64>()
|
||||
});
|
||||
if let Some(company) = state
|
||||
.companies
|
||||
.iter_mut()
|
||||
.find(|company| company.company_id == company_id)
|
||||
{
|
||||
company.current_cash = 0;
|
||||
company.debt = 0;
|
||||
company.active = false;
|
||||
if state.selected_company_id == Some(company_id) {
|
||||
state.selected_company_id = None;
|
||||
if let Some(remaining_debt) = remaining_debt {
|
||||
company.debt = remaining_debt.min(u64::from(u32::MAX)) as u64;
|
||||
}
|
||||
company_mutated = true;
|
||||
}
|
||||
|
||||
if let Some(market_state) = state.service_state.company_market_state.get_mut(&company_id) {
|
||||
market_state.last_bankruptcy_year = bankruptcy_year;
|
||||
market_state.bond_count = 0;
|
||||
market_state.largest_live_bond_principal = None;
|
||||
market_state.highest_coupon_live_bond_principal = None;
|
||||
market_state.live_bond_slots.clear();
|
||||
company_mutated = true;
|
||||
}
|
||||
|
||||
let retired_company_ids = vec![company_id];
|
||||
retire_matching_trains(&mut state.trains, Some(&retired_company_ids), None, None);
|
||||
company_mutated
|
||||
}
|
||||
|
||||
|
|
@ -3143,20 +3165,32 @@ mod tests {
|
|||
state.service_state.annual_finance_last_actions.get(&31),
|
||||
Some(&crate::RuntimeCompanyAnnualFinancePolicyAction::CreditorPressureBankruptcy)
|
||||
);
|
||||
assert!(!state.companies[0].active);
|
||||
assert_eq!(state.companies[0].current_cash, 0);
|
||||
assert_eq!(state.companies[0].debt, 0);
|
||||
assert_eq!(state.selected_company_id, None);
|
||||
assert!(state.trains[0].retired);
|
||||
assert_eq!(state.companies[0].debt, 250_000);
|
||||
assert!(state.companies[0].active);
|
||||
assert_eq!(state.selected_company_id, Some(31));
|
||||
assert!(!state.trains[0].retired);
|
||||
assert_eq!(
|
||||
state.service_state.company_market_state[&31].last_bankruptcy_year,
|
||||
1845
|
||||
);
|
||||
assert_eq!(state.service_state.company_market_state[&31].bond_count, 0);
|
||||
assert_eq!(state.service_state.company_market_state[&31].bond_count, 1);
|
||||
assert_eq!(
|
||||
state.service_state.company_market_state[&31].largest_live_bond_principal,
|
||||
Some(250_000)
|
||||
);
|
||||
assert_eq!(
|
||||
state.service_state.company_market_state[&31].highest_coupon_live_bond_principal,
|
||||
Some(250_000)
|
||||
);
|
||||
assert!(
|
||||
state.service_state.company_market_state[&31]
|
||||
.live_bond_slots
|
||||
.is_empty()
|
||||
state.service_state.company_market_state[&31].live_bond_slots
|
||||
== vec![crate::RuntimeCompanyBondSlot {
|
||||
slot_index: 0,
|
||||
principal: 250_000,
|
||||
maturity_year: 0,
|
||||
coupon_rate_raw_u32: 0.08f32.to_bits(),
|
||||
}]
|
||||
);
|
||||
assert!(
|
||||
result
|
||||
|
|
@ -3298,20 +3332,32 @@ mod tests {
|
|||
state.service_state.annual_finance_last_actions.get(&32),
|
||||
Some(&crate::RuntimeCompanyAnnualFinancePolicyAction::DeepDistressBankruptcyFallback)
|
||||
);
|
||||
assert!(!state.companies[0].active);
|
||||
assert_eq!(state.companies[0].current_cash, 0);
|
||||
assert_eq!(state.companies[0].debt, 0);
|
||||
assert_eq!(state.selected_company_id, None);
|
||||
assert!(state.trains[0].retired);
|
||||
assert_eq!(state.companies[0].debt, 125_000);
|
||||
assert!(state.companies[0].active);
|
||||
assert_eq!(state.selected_company_id, Some(32));
|
||||
assert!(!state.trains[0].retired);
|
||||
assert_eq!(
|
||||
state.service_state.company_market_state[&32].last_bankruptcy_year,
|
||||
1845
|
||||
);
|
||||
assert_eq!(state.service_state.company_market_state[&32].bond_count, 0);
|
||||
assert_eq!(state.service_state.company_market_state[&32].bond_count, 1);
|
||||
assert_eq!(
|
||||
state.service_state.company_market_state[&32].largest_live_bond_principal,
|
||||
Some(125_000)
|
||||
);
|
||||
assert_eq!(
|
||||
state.service_state.company_market_state[&32].highest_coupon_live_bond_principal,
|
||||
Some(125_000)
|
||||
);
|
||||
assert!(
|
||||
state.service_state.company_market_state[&32]
|
||||
.live_bond_slots
|
||||
.is_empty()
|
||||
state.service_state.company_market_state[&32].live_bond_slots
|
||||
== vec![crate::RuntimeCompanyBondSlot {
|
||||
slot_index: 0,
|
||||
principal: 125_000,
|
||||
maturity_year: 0,
|
||||
coupon_rate_raw_u32: 0.07f32.to_bits(),
|
||||
}]
|
||||
);
|
||||
assert!(
|
||||
result
|
||||
|
|
|
|||
|
|
@ -150,7 +150,9 @@ The highest-value next passes are now:
|
|||
stock-repurchase gate headlessly as another pure reader; periodic boundary service now also
|
||||
chooses one annual-finance action per active company and already commits the shellless
|
||||
creditor-pressure-bankruptcy, deep-distress-bankruptcy, dividend-adjustment, stock-repurchase,
|
||||
stock-issue, and bond-issue branches against owned runtime state; the same live bond-slot owner
|
||||
stock-issue, and bond-issue branches against owned runtime state, with bankruptcy now following
|
||||
the grounded “halve live bond debt and stamp the year” path rather than a liquidation shortcut;
|
||||
the same live bond-slot owner
|
||||
surface now also carries save-native maturity years into annual bond policy summaries as the
|
||||
next seam for shellless repayment work, and now also derives the current live coupon burden
|
||||
directly from owned bond slots
|
||||
|
|
|
|||
|
|
@ -247,6 +247,8 @@ the runtime selects one annual-finance action per active company and already com
|
|||
creditor-pressure-bankruptcy, deep-distress-bankruptcy, dividend-adjustment, stock-repurchase,
|
||||
stock-issue, and bond-issue branches directly into owned dividend, company stat-post,
|
||||
outstanding-share, issue-calendar, live bond-slot, and company activity state.
|
||||
The bankruptcy branches now follow the grounded owner semantics too: they stamp the bankruptcy
|
||||
year and halve live bond principals in place instead of collapsing into a liquidation-only path.
|
||||
The same owned live bond-slot surface now also carries maturity years through save import,
|
||||
runtime state, and annual bond summaries, which is the right next base for shellless repayment and
|
||||
bond-service simulation.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue