Add symbolic company target runtime import
This commit is contained in:
parent
4ff6d65774
commit
f918d0c4f7
17 changed files with 1230 additions and 80 deletions
|
|
@ -3,7 +3,8 @@ use std::collections::BTreeSet;
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
RuntimeCompanyTarget, RuntimeEffect, RuntimeEventRecordTemplate, RuntimeState, RuntimeSummary,
|
||||
RuntimeCompanyControllerKind, RuntimeCompanyTarget, RuntimeEffect, RuntimeEventRecordTemplate,
|
||||
RuntimeState, RuntimeSummary,
|
||||
calendar::BoundaryEventKind,
|
||||
};
|
||||
|
||||
|
|
@ -430,6 +431,49 @@ fn resolve_company_target_ids(
|
|||
}
|
||||
Ok(ids.clone())
|
||||
}
|
||||
RuntimeCompanyTarget::HumanCompanies => {
|
||||
if state
|
||||
.companies
|
||||
.iter()
|
||||
.any(|company| company.controller_kind == RuntimeCompanyControllerKind::Unknown)
|
||||
{
|
||||
return Err(
|
||||
"target requires company role context but at least one company has unknown controller_kind"
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
Ok(state
|
||||
.companies
|
||||
.iter()
|
||||
.filter(|company| company.controller_kind == RuntimeCompanyControllerKind::Human)
|
||||
.map(|company| company.company_id)
|
||||
.collect())
|
||||
}
|
||||
RuntimeCompanyTarget::AiCompanies => {
|
||||
if state
|
||||
.companies
|
||||
.iter()
|
||||
.any(|company| company.controller_kind == RuntimeCompanyControllerKind::Unknown)
|
||||
{
|
||||
return Err(
|
||||
"target requires company role context but at least one company has unknown controller_kind"
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
Ok(state
|
||||
.companies
|
||||
.iter()
|
||||
.filter(|company| company.controller_kind == RuntimeCompanyControllerKind::Ai)
|
||||
.map(|company| company.company_id)
|
||||
.collect())
|
||||
}
|
||||
RuntimeCompanyTarget::SelectedCompany => state
|
||||
.selected_company_id
|
||||
.map(|company_id| vec![company_id])
|
||||
.ok_or_else(|| "target requires selected_company_id context".to_string()),
|
||||
RuntimeCompanyTarget::ConditionTrueCompany => {
|
||||
Err("target requires condition-evaluation context".to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -451,9 +495,9 @@ mod tests {
|
|||
|
||||
use super::*;
|
||||
use crate::{
|
||||
CalendarPoint, RuntimeCompany, RuntimeCompanyTarget, RuntimeEffect, RuntimeEventRecord,
|
||||
RuntimeEventRecordTemplate, RuntimeSaveProfileState, RuntimeServiceState,
|
||||
RuntimeWorldRestoreState,
|
||||
CalendarPoint, RuntimeCompany, RuntimeCompanyControllerKind, RuntimeCompanyTarget,
|
||||
RuntimeEffect, RuntimeEventRecord, RuntimeEventRecordTemplate, RuntimeSaveProfileState,
|
||||
RuntimeServiceState, RuntimeWorldRestoreState,
|
||||
};
|
||||
|
||||
fn state() -> RuntimeState {
|
||||
|
|
@ -470,9 +514,11 @@ mod tests {
|
|||
metadata: BTreeMap::new(),
|
||||
companies: vec![RuntimeCompany {
|
||||
company_id: 1,
|
||||
controller_kind: RuntimeCompanyControllerKind::Unknown,
|
||||
current_cash: 10,
|
||||
debt: 0,
|
||||
}],
|
||||
selected_company_id: None,
|
||||
packed_event_collection: None,
|
||||
event_runtime_records: Vec::new(),
|
||||
candidate_availability: BTreeMap::new(),
|
||||
|
|
@ -630,11 +676,13 @@ mod tests {
|
|||
companies: vec![
|
||||
RuntimeCompany {
|
||||
company_id: 1,
|
||||
controller_kind: RuntimeCompanyControllerKind::Unknown,
|
||||
current_cash: 10,
|
||||
debt: 5,
|
||||
},
|
||||
RuntimeCompany {
|
||||
company_id: 2,
|
||||
controller_kind: RuntimeCompanyControllerKind::Unknown,
|
||||
current_cash: 20,
|
||||
debt: 8,
|
||||
},
|
||||
|
|
@ -674,6 +722,165 @@ mod tests {
|
|||
assert_eq!(result.service_events[0].mutated_company_ids, vec![2]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolves_symbolic_company_targets() {
|
||||
let mut state = RuntimeState {
|
||||
companies: vec![
|
||||
RuntimeCompany {
|
||||
company_id: 1,
|
||||
controller_kind: RuntimeCompanyControllerKind::Human,
|
||||
current_cash: 10,
|
||||
debt: 0,
|
||||
},
|
||||
RuntimeCompany {
|
||||
company_id: 2,
|
||||
controller_kind: RuntimeCompanyControllerKind::Ai,
|
||||
current_cash: 20,
|
||||
debt: 2,
|
||||
},
|
||||
],
|
||||
selected_company_id: Some(1),
|
||||
event_runtime_records: vec![
|
||||
RuntimeEventRecord {
|
||||
record_id: 11,
|
||||
trigger_kind: 7,
|
||||
active: true,
|
||||
service_count: 0,
|
||||
marks_collection_dirty: false,
|
||||
one_shot: false,
|
||||
has_fired: false,
|
||||
effects: vec![RuntimeEffect::AdjustCompanyCash {
|
||||
target: RuntimeCompanyTarget::HumanCompanies,
|
||||
delta: 5,
|
||||
}],
|
||||
},
|
||||
RuntimeEventRecord {
|
||||
record_id: 12,
|
||||
trigger_kind: 7,
|
||||
active: true,
|
||||
service_count: 0,
|
||||
marks_collection_dirty: false,
|
||||
one_shot: false,
|
||||
has_fired: false,
|
||||
effects: vec![RuntimeEffect::AdjustCompanyDebt {
|
||||
target: RuntimeCompanyTarget::AiCompanies,
|
||||
delta: 3,
|
||||
}],
|
||||
},
|
||||
RuntimeEventRecord {
|
||||
record_id: 13,
|
||||
trigger_kind: 7,
|
||||
active: true,
|
||||
service_count: 0,
|
||||
marks_collection_dirty: false,
|
||||
one_shot: false,
|
||||
has_fired: false,
|
||||
effects: vec![RuntimeEffect::AdjustCompanyCash {
|
||||
target: RuntimeCompanyTarget::SelectedCompany,
|
||||
delta: 7,
|
||||
}],
|
||||
},
|
||||
],
|
||||
..state()
|
||||
};
|
||||
|
||||
let result = execute_step_command(
|
||||
&mut state,
|
||||
&StepCommand::ServiceTriggerKind { trigger_kind: 7 },
|
||||
)
|
||||
.expect("symbolic target effects should succeed");
|
||||
|
||||
assert_eq!(state.companies[0].current_cash, 22);
|
||||
assert_eq!(state.companies[0].debt, 0);
|
||||
assert_eq!(state.companies[1].current_cash, 20);
|
||||
assert_eq!(state.companies[1].debt, 5);
|
||||
assert_eq!(result.service_events[0].mutated_company_ids, vec![1, 2]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_selected_company_target_without_selection_context() {
|
||||
let mut state = RuntimeState {
|
||||
event_runtime_records: vec![RuntimeEventRecord {
|
||||
record_id: 14,
|
||||
trigger_kind: 7,
|
||||
active: true,
|
||||
service_count: 0,
|
||||
marks_collection_dirty: false,
|
||||
one_shot: false,
|
||||
has_fired: false,
|
||||
effects: vec![RuntimeEffect::AdjustCompanyCash {
|
||||
target: RuntimeCompanyTarget::SelectedCompany,
|
||||
delta: 1,
|
||||
}],
|
||||
}],
|
||||
..state()
|
||||
};
|
||||
|
||||
let error = execute_step_command(
|
||||
&mut state,
|
||||
&StepCommand::ServiceTriggerKind { trigger_kind: 7 },
|
||||
)
|
||||
.expect_err("selected company target should require selection context");
|
||||
|
||||
assert!(error.contains("selected_company_id"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_human_or_ai_targets_without_role_context() {
|
||||
let mut state = RuntimeState {
|
||||
event_runtime_records: vec![RuntimeEventRecord {
|
||||
record_id: 15,
|
||||
trigger_kind: 7,
|
||||
active: true,
|
||||
service_count: 0,
|
||||
marks_collection_dirty: false,
|
||||
one_shot: false,
|
||||
has_fired: false,
|
||||
effects: vec![RuntimeEffect::AdjustCompanyCash {
|
||||
target: RuntimeCompanyTarget::HumanCompanies,
|
||||
delta: 1,
|
||||
}],
|
||||
}],
|
||||
..state()
|
||||
};
|
||||
|
||||
let error = execute_step_command(
|
||||
&mut state,
|
||||
&StepCommand::ServiceTriggerKind { trigger_kind: 7 },
|
||||
)
|
||||
.expect_err("human target should require controller metadata");
|
||||
|
||||
assert!(error.contains("controller_kind"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_condition_true_company_target_without_condition_context() {
|
||||
let mut state = RuntimeState {
|
||||
event_runtime_records: vec![RuntimeEventRecord {
|
||||
record_id: 16,
|
||||
trigger_kind: 7,
|
||||
active: true,
|
||||
service_count: 0,
|
||||
marks_collection_dirty: false,
|
||||
one_shot: false,
|
||||
has_fired: false,
|
||||
effects: vec![RuntimeEffect::AdjustCompanyCash {
|
||||
target: RuntimeCompanyTarget::ConditionTrueCompany,
|
||||
delta: 1,
|
||||
}],
|
||||
}],
|
||||
..state()
|
||||
};
|
||||
|
||||
let error = execute_step_command(
|
||||
&mut state,
|
||||
&StepCommand::ServiceTriggerKind { trigger_kind: 7 },
|
||||
)
|
||||
.expect_err("condition-relative target should remain blocked");
|
||||
|
||||
assert!(error.contains("condition-evaluation context"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn one_shot_record_only_fires_once() {
|
||||
let mut state = RuntimeState {
|
||||
|
|
@ -718,6 +925,7 @@ mod tests {
|
|||
let mut state = RuntimeState {
|
||||
companies: vec![RuntimeCompany {
|
||||
company_id: 1,
|
||||
controller_kind: RuntimeCompanyControllerKind::Unknown,
|
||||
current_cash: 10,
|
||||
debt: 2,
|
||||
}],
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue