use goap::prelude::*;
fn main() {
let initial_state = State::new()
.set("metal", 0) .set("energy", 0) .set("components", 0) .set("battery_charge", 0) .set("defense_rating", 0) .set("has_mine", false)
.set("has_factory", false)
.set("has_solar", false)
.set("has_battery", false)
.set("has_walls", false)
.set("has_turrets", false)
.build();
let goal = Goal::new("build_defensive_base")
.requires("energy", 50) .requires("battery_charge", 100) .requires("defense_rating", 75) .requires("has_walls", true) .requires("has_turrets", true) .build();
let build_mine = Action::new("build_mine")
.cost(3.0)
.sets("has_mine", true)
.adds("metal", 20) .build();
let mine_resources = Action::new("mine_resources")
.cost(2.0)
.requires("has_mine", true)
.adds("metal", 15)
.build();
let build_factory = Action::new("build_factory")
.cost(4.0)
.requires("metal", 20)
.sets("has_factory", true)
.subtracts("metal", 20)
.build();
let craft_components = Action::new("craft_components")
.cost(2.0)
.requires("has_factory", true)
.requires("metal", 5)
.adds("components", 10)
.subtracts("metal", 5)
.build();
let build_solar = Action::new("build_solar_panels")
.cost(3.0)
.requires("components", 5)
.sets("has_solar", true)
.adds("energy", 30)
.subtracts("components", 5)
.build();
let build_battery = Action::new("build_battery")
.cost(3.0)
.requires("components", 8)
.sets("has_battery", true)
.subtracts("components", 8)
.build();
let charge_battery = Action::new("charge_battery")
.cost(2.0)
.requires("has_battery", true)
.requires("has_solar", true)
.requires("energy", 25)
.adds("battery_charge", 50)
.subtracts("energy", 25)
.build();
let build_walls = Action::new("build_walls")
.cost(4.0)
.requires("metal", 15)
.sets("has_walls", true)
.adds("defense_rating", 30)
.subtracts("metal", 15)
.build();
let build_turrets = Action::new("build_turrets")
.cost(5.0)
.requires("components", 10)
.requires("has_walls", true)
.requires("energy", 20)
.sets("has_turrets", true)
.adds("defense_rating", 45)
.subtracts("components", 10)
.build();
let actions = vec![
build_mine,
mine_resources,
build_factory,
craft_components,
build_solar,
build_battery,
charge_battery,
build_walls,
build_turrets,
];
let initial_state_copy = initial_state.clone();
let planner = Planner::new();
let plan_result = planner.plan(initial_state, &goal, &actions);
assert!(
plan_result.is_ok(),
"Expected to find a valid plan for base building"
);
let plan = plan_result.unwrap();
println!(
"\nBase Building Plan found with cost {cost}",
cost = plan.cost
);
for action in &plan.actions {
println!(
"- {name} (cost: {cost})",
name = action.name,
cost = action.cost
);
}
let action_names: Vec<_> = plan.actions.iter().map(|a| a.name.as_str()).collect();
assert!(
action_names.contains(&"build_mine"),
"Plan should include building a mine"
);
assert!(
action_names.contains(&"mine_resources"),
"Plan should include mining resources"
);
assert!(
action_names.contains(&"build_factory"),
"Plan should include building a factory"
);
assert!(
action_names.contains(&"craft_components"),
"Plan should include crafting components"
);
assert!(
action_names.contains(&"build_solar_panels"),
"Plan should include building solar panels"
);
assert!(
action_names.contains(&"build_battery"),
"Plan should include building battery"
);
assert!(
action_names.contains(&"charge_battery"),
"Plan should include charging battery"
);
assert!(
action_names.contains(&"build_walls"),
"Plan should include building walls"
);
assert!(
action_names.contains(&"build_turrets"),
"Plan should include building turrets"
);
let mut current_state = initial_state_copy;
println!("\nSimulating plan execution:");
for action in &plan.actions {
current_state = action.apply_effect(¤t_state);
println!("After {name}: ", name = action.name);
if let Some(metal) = current_state.get::<i64>("metal") {
println!(" Metal: {metal}");
}
if let Some(energy) = current_state.get::<i64>("energy") {
println!(" Energy: {energy}");
}
if let Some(components) = current_state.get::<i64>("components") {
println!(" Components: {components}");
}
if let Some(defense) = current_state.get::<i64>("defense_rating") {
println!(" Defense Rating: {defense}");
}
}
assert!(
current_state.satisfies(&goal.desired_state),
"Final state should meet all goals"
);
if let Some(final_defense) = current_state.get::<i64>("defense_rating") {
assert!(final_defense >= 75, "Defense rating should be at least 75");
}
if let Some(final_energy) = current_state.get::<i64>("energy") {
assert!(
final_energy >= 50,
"Energy production should be at least 50"
);
}
if let Some(final_battery) = current_state.get::<i64>("battery_charge") {
assert!(
final_battery >= 100,
"Battery charge should be at least 100"
);
}
println!("\nFinal base state verification:");
let energy_prod = current_state.get::<i64>("energy");
println!("Energy Production: {energy_prod:?}");
let battery_charge = current_state.get::<i64>("battery_charge");
println!("Battery Charge: {battery_charge:?}");
let defense_rating = current_state.get::<i64>("defense_rating");
println!("Defense Rating: {defense_rating:?}");
let has_walls = current_state.get::<bool>("has_walls");
println!("Has Walls: {has_walls:?}");
let has_turrets = current_state.get::<bool>("has_turrets");
println!("Has Turrets: {has_turrets:?}");
}