Rehost bankruptcy debt-halving finance path

This commit is contained in:
Jan Petykiewicz 2026-04-18 01:48:09 -07:00
commit ad048f1528
4 changed files with 85 additions and 33 deletions

View file

@ -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