removing intended_commands

This commit is contained in:
2026-01-23 16:40:07 -07:00
parent dd24f686b3
commit 9956c930cd
3 changed files with 37 additions and 102 deletions

View File

@@ -1,10 +1,9 @@
use rsbwapi::{Order, Position, Unit, UnitType, UpgradeType}; use rsbwapi::{UnitType, UpgradeType};
use std::collections::HashMap; use std::collections::HashMap;
use crate::state::build_stages::BuildStage; use crate::state::build_stages::BuildStage;
pub struct GameState { pub struct GameState {
pub intended_commands: HashMap<usize, IntendedCommand>,
pub unit_build_history: Vec<BuildHistoryEntry>, pub unit_build_history: Vec<BuildHistoryEntry>,
pub build_stages: Vec<BuildStage>, pub build_stages: Vec<BuildStage>,
pub current_stage_index: usize, pub current_stage_index: usize,
@@ -15,7 +14,6 @@ pub struct GameState {
impl Default for GameState { impl Default for GameState {
fn default() -> Self { fn default() -> Self {
Self { Self {
intended_commands: HashMap::new(),
unit_build_history: Vec::new(), unit_build_history: Vec::new(),
build_stages: crate::state::build_stages::get_build_stages(), build_stages: crate::state::build_stages::get_build_stages(),
current_stage_index: 0, current_stage_index: 0,
@@ -25,13 +23,6 @@ impl Default for GameState {
} }
} }
#[derive(Clone, Debug)]
pub struct IntendedCommand {
pub order: Order,
pub target_position: Option<Position>,
pub target_unit: Option<Unit>,
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum BuildStatus { pub enum BuildStatus {
Assigned, Assigned,

View File

@@ -1,65 +1,25 @@
use rsbwapi::{Game, Order, Player, Unit, UnitType}; use rsbwapi::{Game, Player, Unit, UnitType};
use crate::{ use crate::{
state::game_state::{BuildHistoryEntry, GameState, IntendedCommand}, state::game_state::{BuildHistoryEntry, GameState},
utils::build_location_utils, utils::build_location_utils,
}; };
pub fn on_frame(game: &Game, player: &Player, state: &mut GameState) { pub fn on_frame(game: &Game, player: &Player, state: &mut GameState) {
cleanup_stale_commands(player, state); // No intended command tracking; directly manage builds and stages.
check_and_advance_stage(player, state); check_and_advance_stage(player, state);
state.stage_item_status = get_status_for_stage_items(game, player, state); state.stage_item_status = get_status_for_stage_items(game, player, state);
try_start_next_build(game, player, state); try_start_next_build(game, player, state);
} }
pub fn on_building_create(unit: &Unit, state: &mut GameState) { pub fn on_building_create(_unit: &Unit, _state: &mut GameState) {
// When a building is created, clear any intended command for the worker that was assigned to build it. // No intended command tracking needed.
if let Some(entry) = state
.unit_build_history
.iter()
.rev()
.find(|e| e.unit_type == Some(unit.get_type()))
{
if let Some(builder_id) = entry.assigned_unit_id {
state.intended_commands.remove(&builder_id);
println!(
"Building {} started. Removed assignment for worker {}",
unit.get_type().name(),
builder_id
);
}
}
} }
/// Called when a nonbuilding unit (e.g., a trained unit) is created. /// Called when a nonbuilding unit (e.g., a trained unit) is created.
/// This clears any pending assignment for the building that trained it. /// This clears any pending assignment for the building that trained it.
pub fn on_unit_create(unit: &Unit, state: &mut GameState) { pub fn on_unit_create(_unit: &Unit, _state: &mut GameState) {
// Find the most recent history entry for this unit type. // No intended command tracking needed for unit creation.
if let Some(entry) = state
.unit_build_history
.iter()
.rev()
.find(|e| e.unit_type == Some(unit.get_type()))
{
if let Some(builder_id) = entry.assigned_unit_id {
// For training, we didn't store an intended command, but clear just in case.
state.intended_commands.remove(&builder_id);
println!(
"Unit {} created. Cleared assignment for builder {}",
unit.get_type().name(),
builder_id
);
}
}
}
fn cleanup_stale_commands(player: &Player, state: &mut GameState) {
// Retain intended commands as long as the unit still exists.
// The command will be cleared when the building/unit is created (on_building_create) or when the unit dies.
let unit_ids: Vec<usize> = player.get_units().iter().map(|u| u.get_id()).collect();
state
.intended_commands
.retain(|unit_id, _cmd| unit_ids.contains(unit_id));
} }
fn try_start_next_build(game: &Game, player: &Player, state: &mut GameState) { fn try_start_next_build(game: &Game, player: &Player, state: &mut GameState) {
@@ -84,7 +44,6 @@ fn try_start_next_build(game: &Game, player: &Player, state: &mut GameState) {
tile_position: tile_pos, tile_position: tile_pos,
}; };
state.unit_build_history.push(entry); state.unit_build_history.push(entry);
let current_stage = &state.build_stages[state.current_stage_index];
} }
} }
} }
@@ -94,8 +53,6 @@ fn should_start_next_build(_game: &Game, player: &Player, state: &mut GameState)
!has_ongoing_constructions(state, player) !has_ongoing_constructions(state, player)
} }
// Removed: pending assignment tracking now relies on actual unit state via has_ongoing_constructions.
fn has_ongoing_constructions(state: &GameState, player: &Player) -> bool { fn has_ongoing_constructions(state: &GameState, player: &Player) -> bool {
state.unit_build_history.iter().any(|entry| { state.unit_build_history.iter().any(|entry| {
if let Some(unit_id) = entry.assigned_unit_id { if let Some(unit_id) = entry.assigned_unit_id {
@@ -200,7 +157,7 @@ fn check_need_more_supply(_game: &Game, player: &Player, _state: &GameState) ->
fn find_builder_for_unit( fn find_builder_for_unit(
player: &Player, player: &Player,
unit_type: UnitType, unit_type: UnitType,
state: &GameState, _state: &GameState,
) -> Option<rsbwapi::Unit> { ) -> Option<rsbwapi::Unit> {
let builder_type = unit_type.what_builds().0; let builder_type = unit_type.what_builds().0;
player player
@@ -211,7 +168,6 @@ fn find_builder_for_unit(
&& !u.is_constructing() && !u.is_constructing()
&& !u.is_training() && !u.is_training()
&& (u.is_idle() || u.is_gathering_minerals() || u.is_gathering_gas()) && (u.is_idle() || u.is_gathering_minerals() || u.is_gathering_gas())
&& !state.intended_commands.contains_key(&u.get_id())
}) })
.cloned() .cloned()
} }

View File

@@ -1,17 +1,28 @@
use rsbwapi::{Game, Order, Player, Unit}; use rsbwapi::{Game, Player, Unit};
use crate::state::game_state::{GameState, IntendedCommand}; use crate::state::game_state::GameState;
pub fn assign_idle_workers_to_minerals(game: &Game, player: &Player, state: &mut GameState) { pub fn assign_idle_workers_to_minerals(game: &Game, player: &Player, state: &mut GameState) {
let all_units = player.get_units(); let all_units = player.get_units();
let workers: Vec<Unit> = all_units let workers: Vec<Unit> = all_units
.iter() .iter()
.filter(|u| u.get_type().is_worker() && u.is_completed()) .filter(|u| {
// Worker must be a completed worker unit
u.get_type().is_worker() && u.is_completed()
})
.cloned() .cloned()
.collect(); .collect();
// Assign idle workers to mining // Assign idle workers that are not already assigned in the build history
for worker in workers { for worker in workers {
// Skip if this worker is already recorded as assigned in any ongoing build history entry
let already_assigned = state
.unit_build_history
.iter()
.any(|entry| entry.assigned_unit_id == Some(worker.get_id()));
if already_assigned {
continue;
}
assign_worker_to_mineral(game, &worker, state); assign_worker_to_mineral(game, &worker, state);
} }
} }
@@ -19,32 +30,26 @@ pub fn assign_idle_workers_to_minerals(game: &Game, player: &Player, state: &mut
fn assign_worker_to_mineral(game: &Game, worker: &Unit, state: &mut GameState) { fn assign_worker_to_mineral(game: &Game, worker: &Unit, state: &mut GameState) {
let worker_id = worker.get_id(); let worker_id = worker.get_id();
if let Some(cmd) = state.intended_commands.get(&worker_id) { // Skip if the worker is currently assigned to an inprogress building construction.
if cmd.order != Order::MiningMinerals { let has_in_progress_build = state.unit_build_history.iter().any(|entry| {
return; entry.assigned_unit_id == Some(worker_id)
} && entry.unit_type.map(|ut| ut.is_building()).unwrap_or(false)
} });
if has_in_progress_build {
if !worker.is_idle() {
return; return;
} }
if worker.is_gathering_minerals() || worker.is_gathering_gas() { // Worker must be idle and not already gathering resources.
if !worker.is_idle() || worker.is_gathering_minerals() || worker.is_gathering_gas() {
return; return;
} }
// Find a mineral patch to assign.
let Some(mineral) = find_available_mineral(game, worker, state) else { let Some(mineral) = find_available_mineral(game, worker, state) else {
return; return;
}; };
let intended_cmd = IntendedCommand { // Assign worker to gather minerals (ignore intended command tracking).
order: Order::MiningMinerals,
target_position: None,
target_unit: Some(mineral.clone()),
};
state.intended_commands.insert(worker_id, intended_cmd);
if worker.gather(&mineral).is_ok() { if worker.gather(&mineral).is_ok() {
println!( println!(
"Assigned worker {} to mine from mineral at {:?}", "Assigned worker {} to mine from mineral at {:?}",
@@ -54,34 +59,17 @@ fn assign_worker_to_mineral(game: &Game, worker: &Unit, state: &mut GameState) {
} }
} }
fn find_available_mineral(game: &Game, worker: &Unit, state: &GameState) -> Option<Unit> { fn find_available_mineral(game: &Game, worker: &Unit, _state: &GameState) -> Option<Unit> {
let worker_pos = worker.get_position(); let worker_pos = worker.get_position();
let minerals = game.get_static_minerals(); let minerals = game.get_static_minerals();
let mut mineral_list: Vec<Unit> = minerals.iter().filter(|m| m.exists()).cloned().collect(); let mut mineral_list: Vec<Unit> = minerals.iter().filter(|m| m.exists()).cloned().collect();
// Sort minerals by distance to the worker.
mineral_list.sort_by_key(|m| { mineral_list.sort_by_key(|m| {
let pos = m.get_position(); let pos = m.get_position();
((pos.x - worker_pos.x).pow(2) + (pos.y - worker_pos.y).pow(2)) as i32 ((pos.x - worker_pos.x).pow(2) + (pos.y - worker_pos.y).pow(2)) as i32
}); });
for mineral in mineral_list.iter() { // Return the closest mineral, ignoring any intended command tracking.
let mineral_id: usize = mineral.get_id();
let worker_count = state
.intended_commands
.values()
.filter(|cmd| {
if let Some(target) = &cmd.target_unit {
target.get_id() == mineral_id
} else {
false
}
})
.count();
if worker_count < 2 {
return Some(mineral.clone());
}
}
mineral_list.first().cloned() mineral_list.first().cloned()
} }