Derive annual finance timing inputs
This commit is contained in:
parent
53f1078a84
commit
08f44debc7
6 changed files with 194 additions and 3 deletions
|
|
@ -71,7 +71,9 @@ so future issue-`0x38/0x39` closure can build on a broader owned restore-state w
|
|||
another narrow one-off probe. The next company-side seam is now bundled too: a shared company
|
||||
market reader now exposes outstanding shares, assigned shares, public float, rounded cached share
|
||||
price, salary lanes, bonus amount, and issue-calendar words from the owned annual-finance state
|
||||
instead of leaving that logic spread across summary helpers. A checked-in
|
||||
instead of leaving that logic spread across summary helpers. The same annual-finance state now also
|
||||
derives elapsed years since founding, last dividend, and last bankruptcy from the runtime calendar,
|
||||
which lines up directly with the grounded annual finance-policy gates in the atlas. A checked-in
|
||||
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
|
||||
guessing one more derived leaf field from nearby offsets. A checked-in
|
||||
|
|
|
|||
|
|
@ -108,6 +108,18 @@ pub struct ExpectedRuntimeSummary {
|
|||
#[serde(default)]
|
||||
pub selected_company_stat_band_root_1c47_count: Option<usize>,
|
||||
#[serde(default)]
|
||||
pub selected_company_last_dividend_year: Option<u32>,
|
||||
#[serde(default)]
|
||||
pub selected_company_years_since_founding: Option<u32>,
|
||||
#[serde(default)]
|
||||
pub selected_company_years_since_last_bankruptcy: Option<u32>,
|
||||
#[serde(default)]
|
||||
pub selected_company_years_since_last_dividend: Option<u32>,
|
||||
#[serde(default)]
|
||||
pub selected_company_chairman_bonus_year: Option<u32>,
|
||||
#[serde(default)]
|
||||
pub selected_company_chairman_bonus_amount: Option<i32>,
|
||||
#[serde(default)]
|
||||
pub player_count: Option<usize>,
|
||||
#[serde(default)]
|
||||
pub chairman_profile_count: Option<usize>,
|
||||
|
|
@ -669,6 +681,54 @@ impl ExpectedRuntimeSummary {
|
|||
));
|
||||
}
|
||||
}
|
||||
if let Some(value) = self.selected_company_last_dividend_year {
|
||||
if actual.selected_company_last_dividend_year != Some(value) {
|
||||
mismatches.push(format!(
|
||||
"selected_company_last_dividend_year mismatch: expected {value}, got {:?}",
|
||||
actual.selected_company_last_dividend_year
|
||||
));
|
||||
}
|
||||
}
|
||||
if let Some(value) = self.selected_company_years_since_founding {
|
||||
if actual.selected_company_years_since_founding != Some(value) {
|
||||
mismatches.push(format!(
|
||||
"selected_company_years_since_founding mismatch: expected {value}, got {:?}",
|
||||
actual.selected_company_years_since_founding
|
||||
));
|
||||
}
|
||||
}
|
||||
if let Some(value) = self.selected_company_years_since_last_bankruptcy {
|
||||
if actual.selected_company_years_since_last_bankruptcy != Some(value) {
|
||||
mismatches.push(format!(
|
||||
"selected_company_years_since_last_bankruptcy mismatch: expected {value}, got {:?}",
|
||||
actual.selected_company_years_since_last_bankruptcy
|
||||
));
|
||||
}
|
||||
}
|
||||
if let Some(value) = self.selected_company_years_since_last_dividend {
|
||||
if actual.selected_company_years_since_last_dividend != Some(value) {
|
||||
mismatches.push(format!(
|
||||
"selected_company_years_since_last_dividend mismatch: expected {value}, got {:?}",
|
||||
actual.selected_company_years_since_last_dividend
|
||||
));
|
||||
}
|
||||
}
|
||||
if let Some(value) = self.selected_company_chairman_bonus_year {
|
||||
if actual.selected_company_chairman_bonus_year != Some(value) {
|
||||
mismatches.push(format!(
|
||||
"selected_company_chairman_bonus_year mismatch: expected {value}, got {:?}",
|
||||
actual.selected_company_chairman_bonus_year
|
||||
));
|
||||
}
|
||||
}
|
||||
if let Some(value) = self.selected_company_chairman_bonus_amount {
|
||||
if actual.selected_company_chairman_bonus_amount != Some(value) {
|
||||
mismatches.push(format!(
|
||||
"selected_company_chairman_bonus_amount mismatch: expected {value}, got {:?}",
|
||||
actual.selected_company_chairman_bonus_amount
|
||||
));
|
||||
}
|
||||
}
|
||||
if let Some(count) = self.player_count {
|
||||
if actual.player_count != count {
|
||||
mismatches.push(format!(
|
||||
|
|
|
|||
|
|
@ -107,6 +107,12 @@ pub struct RuntimeCompanyAnnualFinanceState {
|
|||
pub founding_year: u32,
|
||||
pub last_bankruptcy_year: u32,
|
||||
pub last_dividend_year: u32,
|
||||
#[serde(default)]
|
||||
pub years_since_founding: Option<u32>,
|
||||
#[serde(default)]
|
||||
pub years_since_last_bankruptcy: Option<u32>,
|
||||
#[serde(default)]
|
||||
pub years_since_last_dividend: Option<u32>,
|
||||
pub current_issue_calendar_word: u32,
|
||||
pub prior_issue_calendar_word: u32,
|
||||
pub city_connection_latch: bool,
|
||||
|
|
@ -1904,6 +1910,16 @@ pub fn runtime_company_annual_finance_state(
|
|||
let market_state = state.service_state.company_market_state.get(&company_id)?;
|
||||
let assigned_share_pool = runtime_company_assigned_share_pool(state, company_id)?;
|
||||
let unassigned_share_pool = runtime_company_unassigned_share_pool(state, company_id)?;
|
||||
let years_since_founding =
|
||||
derive_runtime_company_elapsed_years(state.calendar.year, market_state.founding_year);
|
||||
let years_since_last_bankruptcy = derive_runtime_company_elapsed_years(
|
||||
state.calendar.year,
|
||||
market_state.last_bankruptcy_year,
|
||||
);
|
||||
let years_since_last_dividend = derive_runtime_company_elapsed_years(
|
||||
state.calendar.year,
|
||||
market_state.last_dividend_year,
|
||||
);
|
||||
Some(RuntimeCompanyAnnualFinanceState {
|
||||
company_id,
|
||||
outstanding_shares: market_state.outstanding_shares,
|
||||
|
|
@ -1917,6 +1933,9 @@ pub fn runtime_company_annual_finance_state(
|
|||
founding_year: market_state.founding_year,
|
||||
last_bankruptcy_year: market_state.last_bankruptcy_year,
|
||||
last_dividend_year: market_state.last_dividend_year,
|
||||
years_since_founding,
|
||||
years_since_last_bankruptcy,
|
||||
years_since_last_dividend,
|
||||
current_issue_calendar_word: market_state.current_issue_calendar_word,
|
||||
prior_issue_calendar_word: market_state.prior_issue_calendar_word,
|
||||
city_connection_latch: market_state.city_connection_latch,
|
||||
|
|
@ -1970,6 +1989,13 @@ fn rounded_cached_share_price_i64(raw_u32: u32) -> Option<i64> {
|
|||
Some(value.round() as i64)
|
||||
}
|
||||
|
||||
fn derive_runtime_company_elapsed_years(current_year: u32, prior_year: u32) -> Option<u32> {
|
||||
if prior_year == 0 || prior_year > current_year {
|
||||
return None;
|
||||
}
|
||||
Some(current_year - prior_year)
|
||||
}
|
||||
|
||||
fn derive_runtime_chairman_holdings_share_price_total(
|
||||
holdings_by_company: &BTreeMap<u32, u32>,
|
||||
company_share_prices: &BTreeMap<u32, i64>,
|
||||
|
|
@ -4231,6 +4257,9 @@ mod tests {
|
|||
founding_year: 1831,
|
||||
last_bankruptcy_year: 0,
|
||||
last_dividend_year: 1841,
|
||||
years_since_founding: None,
|
||||
years_since_last_bankruptcy: None,
|
||||
years_since_last_dividend: None,
|
||||
current_issue_calendar_word: 5,
|
||||
prior_issue_calendar_word: 4,
|
||||
city_connection_latch: true,
|
||||
|
|
@ -4385,4 +4414,85 @@ mod tests {
|
|||
None
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn derives_elapsed_company_finance_years_from_calendar_and_saved_market_state() {
|
||||
let state = RuntimeState {
|
||||
calendar: CalendarPoint {
|
||||
year: 1844,
|
||||
month_slot: 0,
|
||||
phase_slot: 0,
|
||||
tick_slot: 0,
|
||||
},
|
||||
world_flags: BTreeMap::new(),
|
||||
save_profile: RuntimeSaveProfileState::default(),
|
||||
world_restore: RuntimeWorldRestoreState::default(),
|
||||
metadata: BTreeMap::new(),
|
||||
companies: vec![RuntimeCompany {
|
||||
company_id: 3,
|
||||
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([(
|
||||
3,
|
||||
RuntimeCompanyMarketState {
|
||||
outstanding_shares: 10_000,
|
||||
founding_year: 1838,
|
||||
last_bankruptcy_year: 1841,
|
||||
last_dividend_year: 1843,
|
||||
..RuntimeCompanyMarketState::default()
|
||||
},
|
||||
)]),
|
||||
..RuntimeServiceState::default()
|
||||
},
|
||||
};
|
||||
|
||||
let finance_state =
|
||||
runtime_company_annual_finance_state(&state, 3).expect("finance state should derive");
|
||||
assert_eq!(finance_state.years_since_founding, Some(6));
|
||||
assert_eq!(finance_state.years_since_last_bankruptcy, Some(3));
|
||||
assert_eq!(finance_state.years_since_last_dividend, Some(1));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,6 +59,9 @@ pub struct RuntimeSummary {
|
|||
pub selected_company_stat_band_root_0d7f_count: usize,
|
||||
pub selected_company_stat_band_root_1c47_count: usize,
|
||||
pub selected_company_last_dividend_year: Option<u32>,
|
||||
pub selected_company_years_since_founding: Option<u32>,
|
||||
pub selected_company_years_since_last_bankruptcy: Option<u32>,
|
||||
pub selected_company_years_since_last_dividend: Option<u32>,
|
||||
pub selected_company_chairman_bonus_year: Option<u32>,
|
||||
pub selected_company_chairman_bonus_amount: Option<i32>,
|
||||
pub player_count: usize,
|
||||
|
|
@ -282,6 +285,15 @@ impl RuntimeSummary {
|
|||
selected_company_last_dividend_year: selected_company_market_state
|
||||
.map(|market_state| market_state.last_dividend_year)
|
||||
.filter(|year| *year != 0),
|
||||
selected_company_years_since_founding: selected_company_annual_finance_state
|
||||
.as_ref()
|
||||
.and_then(|finance_state| finance_state.years_since_founding),
|
||||
selected_company_years_since_last_bankruptcy: selected_company_annual_finance_state
|
||||
.as_ref()
|
||||
.and_then(|finance_state| finance_state.years_since_last_bankruptcy),
|
||||
selected_company_years_since_last_dividend: selected_company_annual_finance_state
|
||||
.as_ref()
|
||||
.and_then(|finance_state| finance_state.years_since_last_dividend),
|
||||
selected_company_chairman_bonus_year: selected_company_market_state
|
||||
.map(|market_state| market_state.chairman_bonus_year)
|
||||
.filter(|year| *year != 0),
|
||||
|
|
@ -2026,6 +2038,9 @@ mod tests {
|
|||
assert_eq!(summary.selected_company_stat_band_root_0d7f_count, 1);
|
||||
assert_eq!(summary.selected_company_stat_band_root_1c47_count, 1);
|
||||
assert_eq!(summary.selected_company_last_dividend_year, Some(1841));
|
||||
assert_eq!(summary.selected_company_years_since_founding, None);
|
||||
assert_eq!(summary.selected_company_years_since_last_bankruptcy, None);
|
||||
assert_eq!(summary.selected_company_years_since_last_dividend, None);
|
||||
assert_eq!(summary.selected_company_chairman_bonus_year, Some(1842));
|
||||
assert_eq!(summary.selected_company_chairman_bonus_amount, Some(750));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -124,7 +124,9 @@ The highest-value next passes are now:
|
|||
reader seam for assigned shares, public float, and rounded cached share price; the fixed-world
|
||||
finance neighborhood is now widened to 16 dwords rooted at `[world+0x11]` so later issue-family
|
||||
closure can target a broader owned restore-state window; the same annual-finance state now also
|
||||
feeds a shared company market reader for stock-capital, salary, bonus, and issue-calendar values
|
||||
feeds a shared company market reader for stock-capital, salary, bonus, and issue-calendar values,
|
||||
and now derives elapsed years since founding, last dividend, and last bankruptcy for later annual
|
||||
finance-policy rehosting
|
||||
- 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
|
||||
instead of guessing another derived leaf field from neighboring raw offsets
|
||||
|
|
|
|||
|
|
@ -214,7 +214,9 @@ rooted at `[world+0x11]`, so later issue-`0x38/0x39` closure can build on a broa
|
|||
restore-state window instead of another narrow probe. The same owned company annual-finance state
|
||||
now also drives a shared company market reader seam for stock-capital, salary, bonus, and
|
||||
issue-calendar values, which is a better base for shellless finance simulation than summary-only
|
||||
helpers.
|
||||
helpers. That same owned annual-finance state now also derives elapsed years since founding, last
|
||||
dividend, and last bankruptcy from the runtime calendar, which lines up directly with the grounded
|
||||
annual finance-policy gates in the atlas.
|
||||
|
||||
## Why This Boundary
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue