diff --git a/protossbot/src/state/game_state.rs b/protossbot/src/state/game_state.rs index 03ecf23..5a75de7 100644 --- a/protossbot/src/state/game_state.rs +++ b/protossbot/src/state/game_state.rs @@ -1,10 +1,9 @@ -use rsbwapi::{Order, Position, Unit, UnitType, UpgradeType}; +use rsbwapi::{UnitType, UpgradeType}; use std::collections::HashMap; use crate::state::build_stages::BuildStage; pub struct GameState { - pub intended_commands: HashMap, pub unit_build_history: Vec, pub build_stages: Vec, pub current_stage_index: usize, @@ -15,7 +14,6 @@ pub struct GameState { impl Default for GameState { fn default() -> Self { Self { - intended_commands: HashMap::new(), unit_build_history: Vec::new(), build_stages: crate::state::build_stages::get_build_stages(), 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, - pub target_unit: Option, -} - #[derive(Clone, Debug)] pub enum BuildStatus { Assigned, diff --git a/protossbot/src/utils/build_manager.rs b/protossbot/src/utils/build_manager.rs index bc5b331..71b981c 100644 --- a/protossbot/src/utils/build_manager.rs +++ b/protossbot/src/utils/build_manager.rs @@ -1,65 +1,25 @@ -use rsbwapi::{Game, Order, Player, Unit, UnitType}; +use rsbwapi::{Game, Player, Unit, UnitType}; use crate::{ - state::game_state::{BuildHistoryEntry, GameState, IntendedCommand}, + state::game_state::{BuildHistoryEntry, GameState}, utils::build_location_utils, }; 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); state.stage_item_status = get_status_for_stage_items(game, player, state); try_start_next_build(game, player, state); } -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. - 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 - ); - } - } +pub fn on_building_create(_unit: &Unit, _state: &mut GameState) { + // No intended command tracking needed. } /// Called when a non‑building unit (e.g., a trained unit) is created. /// This clears any pending assignment for the building that trained it. -pub fn on_unit_create(unit: &Unit, state: &mut GameState) { - // Find the most recent history entry for this unit type. - 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 = player.get_units().iter().map(|u| u.get_id()).collect(); - state - .intended_commands - .retain(|unit_id, _cmd| unit_ids.contains(unit_id)); +pub fn on_unit_create(_unit: &Unit, _state: &mut GameState) { + // No intended command tracking needed for unit creation. } 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, }; 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) } -// Removed: pending assignment tracking now relies on actual unit state via has_ongoing_constructions. - fn has_ongoing_constructions(state: &GameState, player: &Player) -> bool { state.unit_build_history.iter().any(|entry| { 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( player: &Player, unit_type: UnitType, - state: &GameState, + _state: &GameState, ) -> Option { let builder_type = unit_type.what_builds().0; player @@ -211,7 +168,6 @@ fn find_builder_for_unit( && !u.is_constructing() && !u.is_training() && (u.is_idle() || u.is_gathering_minerals() || u.is_gathering_gas()) - && !state.intended_commands.contains_key(&u.get_id()) }) .cloned() } diff --git a/protossbot/src/utils/worker_management.rs b/protossbot/src/utils/worker_management.rs index 82d1842..ee4d431 100644 --- a/protossbot/src/utils/worker_management.rs +++ b/protossbot/src/utils/worker_management.rs @@ -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) { let all_units = player.get_units(); let workers: Vec = all_units .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() .collect(); - // Assign idle workers to mining + // Assign idle workers that are not already assigned in the build history 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); } } @@ -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) { let worker_id = worker.get_id(); - if let Some(cmd) = state.intended_commands.get(&worker_id) { - if cmd.order != Order::MiningMinerals { - return; - } - } - - if !worker.is_idle() { + // Skip if the worker is currently assigned to an in‑progress building construction. + let has_in_progress_build = state.unit_build_history.iter().any(|entry| { + entry.assigned_unit_id == Some(worker_id) + && entry.unit_type.map(|ut| ut.is_building()).unwrap_or(false) + }); + if has_in_progress_build { 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; } + // Find a mineral patch to assign. let Some(mineral) = find_available_mineral(game, worker, state) else { return; }; - let intended_cmd = IntendedCommand { - order: Order::MiningMinerals, - target_position: None, - target_unit: Some(mineral.clone()), - }; - - state.intended_commands.insert(worker_id, intended_cmd); - + // Assign worker to gather minerals (ignore intended command tracking). if worker.gather(&mineral).is_ok() { println!( "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 { +fn find_available_mineral(game: &Game, worker: &Unit, _state: &GameState) -> Option { let worker_pos = worker.get_position(); let minerals = game.get_static_minerals(); let mut mineral_list: Vec = minerals.iter().filter(|m| m.exists()).cloned().collect(); + // Sort minerals by distance to the worker. mineral_list.sort_by_key(|m| { let pos = m.get_position(); ((pos.x - worker_pos.x).pow(2) + (pos.y - worker_pos.y).pow(2)) as i32 }); - for mineral in mineral_list.iter() { - 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()); - } - } + // Return the closest mineral, ignoring any intended command tracking. mineral_list.first().cloned() }