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 crate::state::build_stages::BuildStage;
pub struct GameState {
pub intended_commands: HashMap<usize, IntendedCommand>,
pub unit_build_history: Vec<BuildHistoryEntry>,
pub build_stages: Vec<BuildStage>,
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<Position>,
pub target_unit: Option<Unit>,
}
#[derive(Clone, Debug)]
pub enum BuildStatus {
Assigned,

View File

@@ -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 nonbuilding 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<usize> = 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<rsbwapi::Unit> {
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()
}

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) {
let all_units = player.get_units();
let workers: Vec<Unit> = 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 inprogress 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<Unit> {
fn find_available_mineral(game: &Game, worker: &Unit, _state: &GameState) -> Option<Unit> {
let worker_pos = worker.get_position();
let minerals = game.get_static_minerals();
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| {
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()
}