Rehost company unassigned share pool

This commit is contained in:
Jan Petykiewicz 2026-04-17 20:55:42 -07:00
commit c41a6b0e92
7 changed files with 189 additions and 13 deletions

View file

@ -60,7 +60,10 @@ extend one shared reader family instead of hard-coding more direct field accesse
stat-band windows are now widened to 16 dwords per root in save-slice/runtime state so later
year-series finance closure has enough owned raw state to attach to. The matching world-side issue
reader seam is now also rehosted for the grounded `0x37` investor-confidence lane on top of the
save-native world-restore state. A checked-in
save-native world-restore state. The selected-company summary path now also exposes the
unassigned share pool derived from outstanding shares minus chairman-held shares, so later
dividend / stock-capital logic can extend one owned market reader instead of another ad hoc
counter. 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

View file

@ -92,6 +92,8 @@ pub struct ExpectedRuntimeSummary {
#[serde(default)]
pub selected_company_outstanding_shares: Option<u32>,
#[serde(default)]
pub selected_company_unassigned_share_pool: Option<u32>,
#[serde(default)]
pub selected_company_cached_share_price_value_f32_text: Option<String>,
#[serde(default)]
pub selected_company_mutable_support_scalar_value_f32_text: Option<String>,
@ -591,6 +593,14 @@ impl ExpectedRuntimeSummary {
));
}
}
if let Some(value) = self.selected_company_unassigned_share_pool {
if actual.selected_company_unassigned_share_pool != Some(value) {
mismatches.push(format!(
"selected_company_unassigned_share_pool mismatch: expected {value}, got {:?}",
actual.selected_company_unassigned_share_pool
));
}
}
if let Some(value) = &self.selected_company_cached_share_price_value_f32_text {
if actual
.selected_company_cached_share_price_value_f32_text

View file

@ -61,7 +61,8 @@ pub use runtime::{
RuntimeWorldFinanceNeighborhoodCandidate, RuntimeWorldIssueState, RuntimeWorldRestoreState,
RUNTIME_COMPANY_STAT_FAMILY_CONTROL_TRANSFER, RUNTIME_COMPANY_STAT_SLOT_BOOK_VALUE_PER_SHARE,
RUNTIME_COMPANY_STAT_SLOT_CURRENT_CASH, RUNTIME_WORLD_ISSUE_INVESTOR_CONFIDENCE,
runtime_company_stat_value, runtime_world_issue_state,
runtime_company_stat_value, runtime_company_unassigned_share_pool,
runtime_world_issue_state,
};
pub use smp::{
SMP_FOUR_SIDECAR_BYTE_PLANES_MIN_BUNDLE_VERSION, SmpAlignedRuntimeRuleBandLane,

View file

@ -1838,6 +1838,20 @@ pub fn runtime_world_issue_state(state: &RuntimeState, issue_id: u32) -> Option<
}
}
pub fn runtime_company_unassigned_share_pool(state: &RuntimeState, company_id: u32) -> Option<u32> {
let outstanding_shares = state
.service_state
.company_market_state
.get(&company_id)?
.outstanding_shares;
let assigned_shares = state
.chairman_profiles
.iter()
.filter_map(|profile| profile.company_holdings.get(&company_id).copied())
.sum::<u32>();
Some(outstanding_shares.saturating_sub(assigned_shares))
}
fn rounded_cached_share_price_i64(raw_u32: u32) -> Option<i64> {
let value = f32::from_bits(raw_u32);
if !value.is_finite() {
@ -3889,4 +3903,102 @@ mod tests {
assert_eq!(issue.multiplier_value_f32_text, "0.060000");
assert_eq!(runtime_world_issue_state(&state, 0x39), None);
}
#[test]
fn derives_company_unassigned_share_pool_from_market_state_and_holdings() {
let state = RuntimeState {
calendar: CalendarPoint {
year: 1830,
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: 4,
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![
RuntimeChairmanProfile {
profile_id: 1,
name: "Chairman One".to_string(),
active: true,
current_cash: 0,
linked_company_id: Some(4),
company_holdings: BTreeMap::from([(4, 8_000)]),
holdings_value_total: 0,
net_worth_total: 0,
purchasing_power_total: 0,
},
RuntimeChairmanProfile {
profile_id: 2,
name: "Chairman Two".to_string(),
active: true,
current_cash: 0,
linked_company_id: None,
company_holdings: BTreeMap::from([(4, 7_500)]),
holdings_value_total: 0,
net_worth_total: 0,
purchasing_power_total: 0,
},
],
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([(
4,
RuntimeCompanyMarketState {
outstanding_shares: 20_000,
..RuntimeCompanyMarketState::default()
},
)]),
..RuntimeServiceState::default()
},
};
assert_eq!(runtime_company_unassigned_share_pool(&state, 4), Some(4_500));
assert_eq!(runtime_company_unassigned_share_pool(&state, 99), None);
}
}

View file

@ -1,6 +1,6 @@
use serde::{Deserialize, Serialize};
use crate::{CalendarPoint, RuntimeState};
use crate::{runtime_company_unassigned_share_pool, CalendarPoint, RuntimeState};
fn raw_u32_to_f32_text(raw: u32) -> String {
format!("{:.6}", f32::from_bits(raw))
@ -47,6 +47,7 @@ pub struct RuntimeSummary {
pub active_company_count: usize,
pub company_market_state_owner_count: usize,
pub selected_company_outstanding_shares: Option<u32>,
pub selected_company_unassigned_share_pool: Option<u32>,
pub selected_company_cached_share_price_value_f32_text: Option<String>,
pub selected_company_mutable_support_scalar_value_f32_text: Option<String>,
pub selected_company_stat_band_root_0cfb_count: usize,
@ -246,6 +247,9 @@ impl RuntimeSummary {
company_market_state_owner_count: state.service_state.company_market_state.len(),
selected_company_outstanding_shares: selected_company_market_state
.map(|market_state| market_state.outstanding_shares),
selected_company_unassigned_share_pool: state
.selected_company_id
.and_then(|company_id| runtime_company_unassigned_share_pool(state, company_id)),
selected_company_cached_share_price_value_f32_text: selected_company_market_state
.map(|market_state| raw_u32_to_f32_text(market_state.cached_share_price_raw_u32)),
selected_company_mutable_support_scalar_value_f32_text: selected_company_market_state
@ -865,8 +869,18 @@ mod tests {
selected_company_id: None,
players: Vec::new(),
selected_player_id: None,
chairman_profiles: Vec::new(),
selected_chairman_profile_id: None,
chairman_profiles: vec![crate::RuntimeChairmanProfile {
profile_id: 1,
name: "Chairman One".to_string(),
active: true,
current_cash: 0,
linked_company_id: Some(1),
company_holdings: std::collections::BTreeMap::from([(1, 5_000)]),
holdings_value_total: 0,
net_worth_total: 0,
purchasing_power_total: 0,
}],
selected_chairman_profile_id: Some(1),
trains: Vec::new(),
locomotive_catalog: vec![
crate::RuntimeLocomotiveCatalogEntry {
@ -1116,8 +1130,18 @@ mod tests {
selected_company_id: None,
players: Vec::new(),
selected_player_id: None,
chairman_profiles: Vec::new(),
selected_chairman_profile_id: None,
chairman_profiles: vec![crate::RuntimeChairmanProfile {
profile_id: 1,
name: "Chairman One".to_string(),
active: true,
current_cash: 0,
linked_company_id: Some(1),
company_holdings: std::collections::BTreeMap::from([(1, 5_000)]),
holdings_value_total: 0,
net_worth_total: 0,
purchasing_power_total: 0,
}],
selected_chairman_profile_id: Some(1),
trains: Vec::new(),
locomotive_catalog: Vec::new(),
cargo_catalog: Vec::new(),
@ -1227,8 +1251,18 @@ mod tests {
selected_company_id: None,
players: Vec::new(),
selected_player_id: None,
chairman_profiles: Vec::new(),
selected_chairman_profile_id: None,
chairman_profiles: vec![crate::RuntimeChairmanProfile {
profile_id: 1,
name: "Chairman One".to_string(),
active: true,
current_cash: 0,
linked_company_id: Some(1),
company_holdings: std::collections::BTreeMap::from([(1, 5_000)]),
holdings_value_total: 0,
net_worth_total: 0,
purchasing_power_total: 0,
}],
selected_chairman_profile_id: Some(1),
trains: Vec::new(),
locomotive_catalog: vec![
crate::RuntimeLocomotiveCatalogEntry {
@ -1855,8 +1889,18 @@ mod tests {
selected_company_id: Some(1),
players: Vec::new(),
selected_player_id: None,
chairman_profiles: Vec::new(),
selected_chairman_profile_id: None,
chairman_profiles: vec![crate::RuntimeChairmanProfile {
profile_id: 1,
name: "Chairman One".to_string(),
active: true,
current_cash: 0,
linked_company_id: Some(1),
company_holdings: std::collections::BTreeMap::from([(1, 5_000)]),
holdings_value_total: 0,
net_worth_total: 0,
purchasing_power_total: 0,
}],
selected_chairman_profile_id: Some(1),
trains: Vec::new(),
locomotive_catalog: Vec::new(),
cargo_catalog: Vec::new(),
@ -1953,6 +1997,7 @@ mod tests {
let summary = RuntimeSummary::from_state(&state);
assert_eq!(summary.company_market_state_owner_count, 1);
assert_eq!(summary.selected_company_outstanding_shares, Some(20_000));
assert_eq!(summary.selected_company_unassigned_share_pool, Some(15_000));
assert_eq!(
summary.selected_company_cached_share_price_value_f32_text,
Some("40.000000".to_string())

View file

@ -118,7 +118,9 @@ The highest-value next passes are now:
owned runtime data instead of one more guessed save offset; the first runtime-side `0x2329`
stat-family reader seam is now also rehosted for slots `0x0d` and `0x1d`, and the saved
stat-band windows themselves now carry 16 dwords per root; the matching world-side issue reader
seam is now rehosted for the grounded `0x37` lane
seam is now rehosted for the grounded `0x37` lane, and selected-company summaries now expose the
unassigned share pool derived from outstanding shares minus chairman-held shares for later annual
finance logic
- 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

View file

@ -203,7 +203,10 @@ the first grounded stat-band root windows at `[company+0x0cfb]`, `[company+0x0d7
for slots `0x0d` and `0x1d`, so later finance readers can target saved owner state and one shared
reader family directly. Those stat-band windows now carry 16 dwords per root in the save-slice and
runtime-owned company market state, and the matching world-side issue reader seam is now rehosted
for the grounded `0x37` lane over save-native world restore state.
for the grounded `0x37` lane over save-native world restore state. The selected-company summary
surface now also carries the unassigned share pool derived from outstanding shares minus
chairman-held shares, so later dividend / stock-capital work can extend a shared owned-state
reader instead of guessing another finance leaf.
## Why This Boundary