diff --git a/protossbot/src/bot.rs b/protossbot/src/bot.rs index cc5de18..8ddcaa3 100644 --- a/protossbot/src/bot.rs +++ b/protossbot/src/bot.rs @@ -70,16 +70,16 @@ impl AiModule for ProtosBot { } println!("unit created: {:?}", unit.get_type()); - // Check if the created unit is a building - if !unit.get_type().is_building() { - return; - } - + // If the created unit is a building, handle building creation; otherwise handle unit creation (e.g., trained units). let Ok(mut locked_state) = self.game_state.lock() else { return; }; - build_manager::on_building_create(&unit, &mut locked_state); + if unit.get_type().is_building() { + build_manager::on_building_create(&unit, &mut locked_state); + } else { + build_manager::on_unit_create(&unit, &mut locked_state); + } } fn on_unit_morph(&mut self, _game: &Game, _unit: Unit) {} diff --git a/protossbot/src/state/build_stages.rs b/protossbot/src/state/build_stages.rs index e826e49..1b9d544 100644 --- a/protossbot/src/state/build_stages.rs +++ b/protossbot/src/state/build_stages.rs @@ -31,13 +31,19 @@ pub fn get_build_stages() -> Vec { .with_unit(UnitType::Terran_Supply_Depot, 2) .with_unit(UnitType::Terran_Barracks, 1) .with_unit(UnitType::Terran_Refinery, 1), - // Stage 2: Defense bunker BuildStage::new("Defense Bunker") .with_unit(UnitType::Terran_SCV, 16) .with_unit(UnitType::Terran_Supply_Depot, 3) .with_unit(UnitType::Terran_Command_Center, 1) .with_unit(UnitType::Terran_Barracks, 1) - .with_unit(UnitType::Terran_Refinery, 1) - .with_unit(UnitType::Terran_Bunker, 2), + .with_unit(UnitType::Terran_Refinery, 1), + + BuildStage::new("Mid Game") + .with_unit(UnitType::Terran_SCV, 20) + .with_unit(UnitType::Terran_Supply_Depot, 4) + .with_unit(UnitType::Terran_Command_Center, 2) + .with_unit(UnitType::Terran_Barracks, 2) + .with_unit(UnitType::Terran_Refinery, 2) + .with_unit(UnitType::Terran_Missile_Turret, 2), ] } diff --git a/protossbot/src/utils/build_manager.rs b/protossbot/src/utils/build_manager.rs index 6f787a7..bc5b331 100644 --- a/protossbot/src/utils/build_manager.rs +++ b/protossbot/src/utils/build_manager.rs @@ -13,39 +13,53 @@ pub fn on_frame(game: &Game, player: &Player, 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. if let Some(entry) = state .unit_build_history .iter() .rev() .find(|e| e.unit_type == Some(unit.get_type())) { - if let Some(probe_id) = entry.assigned_unit_id { - state.intended_commands.remove(&probe_id); + if let Some(builder_id) = entry.assigned_unit_id { + state.intended_commands.remove(&builder_id); println!( - "Building {} started. Removed assignment for probe {}", + "Building {} started. Removed assignment for worker {}", unit.get_type().name(), - probe_id + builder_id + ); + } + } +} + +/// 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| { - if !unit_ids.contains(unit_id) { - return false; - } - if let Some(unit) = player.get_units().iter().find(|u| u.get_id() == *unit_id) { - if cmd.order == Order::PlaceBuilding { - return unit.is_constructing() || unit.get_order() == Order::PlaceBuilding; - } - if cmd.order == Order::Train { - return unit.is_training(); - } - } - false - }); + state + .intended_commands + .retain(|unit_id, _cmd| unit_ids.contains(unit_id)); } fn try_start_next_build(game: &Game, player: &Player, state: &mut GameState) { @@ -71,29 +85,16 @@ fn try_start_next_build(game: &Game, player: &Player, state: &mut GameState) { }; state.unit_build_history.push(entry); let current_stage = &state.build_stages[state.current_stage_index]; - println!( - "Started building {} with unit {} (Stage: {})", - unit_type.name(), - builder_id, - current_stage.name - ); } } } -fn should_start_next_build(_game: &Game, _player: &Player, state: &mut GameState) -> bool { - !has_pending_assignment(state) +fn should_start_next_build(_game: &Game, player: &Player, state: &mut GameState) -> bool { + // Only start a new build if there are no ongoing constructions or training actions. + !has_ongoing_constructions(state, player) } -fn has_pending_assignment(state: &GameState) -> bool { - state.unit_build_history.iter().any(|entry| { - if let Some(unit_id) = entry.assigned_unit_id { - state.intended_commands.contains_key(&unit_id) - } else { - false - } - }) -} +// 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| { @@ -237,12 +238,7 @@ fn assign_builder_to_construct( match builder.build(unit_type, pos) { Ok(_) => { println!("Build command succeeded for {}", unit_type.name()); - let intended_cmd = IntendedCommand { - order: Order::PlaceBuilding, - target_position: Some(pos.to_position()), - target_unit: None, - }; - state.intended_commands.insert(builder_id, intended_cmd); + // No intended command for building actions; the builder (worker) will be tracked via ongoing constructions. Some((true, Some(pos))) } Err(e) => { @@ -261,12 +257,7 @@ fn assign_builder_to_construct( } else { match builder.train(unit_type) { Ok(_) => { - let intended_cmd = IntendedCommand { - order: Order::Train, - target_position: None, - target_unit: None, - }; - state.intended_commands.insert(builder_id, intended_cmd); + // No intended command for training; the building will be tracked via ongoing constructions. Some((true, None)) } Err(e) => { @@ -357,91 +348,4 @@ pub fn print_debug_build_status(game: &Game, player: &Player, state: &GameState) y += 10; } } - let has_pending = state.unit_build_history.iter().any(|entry| { - if let Some(unit_id) = entry.assigned_unit_id { - state.intended_commands.contains_key(&unit_id) - } else { - false - } - }); - for entry in &state.unit_build_history { - if let (Some(unit_type), Some(tile_pos), Some(unit_id)) = - (entry.unit_type, entry.tile_position, entry.assigned_unit_id) - { - if state.intended_commands.contains_key(&unit_id) { - let pixel_x = tile_pos.x * 32; - let pixel_y = tile_pos.y * 32; - let width = unit_type.tile_width() * 32; - let height = unit_type.tile_height() * 32; - use rsbwapi::{Color, Position}; - let top_left = Position { - x: pixel_x, - y: pixel_y, - }; - let top_right = Position { - x: pixel_x + width, - y: pixel_y, - }; - let bottom_left = Position { - x: pixel_x, - y: pixel_y + height, - }; - let bottom_right = Position { - x: pixel_x + width, - y: pixel_y + height, - }; - let color = Color::Yellow; - game.draw_line_map(top_left, top_right, color); - game.draw_line_map(top_right, bottom_right, color); - game.draw_line_map(bottom_right, bottom_left, color); - game.draw_line_map(bottom_left, top_left, color); - let center = Position { - x: pixel_x + width / 2, - y: pixel_y + height / 2, - }; - game.draw_text_map(center, &format!("Pending: {}", unit_type.name())); - } - } - } - if has_pending { - use rsbwapi::{Color, Position, TilePosition}; - if let Some(nexus) = player - .get_units() - .iter() - .find(|u| u.get_type() == UnitType::Protoss_Nexus) - { - let nexus_tile = nexus.get_tile_position(); - let radius = 30; - for dx in -radius..=radius { - for dy in -radius..=radius { - let tile_x = nexus_tile.x + dx; - let tile_y = nexus_tile.y + dy; - if tile_x < 0 || tile_y < 0 { - continue; - } - let tile_pos = TilePosition { - x: tile_x, - y: tile_y, - }; - let dist_sq = (dx * dx + dy * dy) as f32; - if dist_sq > (radius * radius) as f32 { - continue; - } - let is_buildable = game.is_buildable(tile_pos); - if !is_buildable { - continue; - } - let has_power = game.has_power(tile_pos, (1, 1)); - let pixel_x = tile_x * 32; - let pixel_y = tile_y * 32; - let center = Position { - x: pixel_x + 16, - y: pixel_y + 16, - }; - let color = if has_power { Color::Green } else { Color::Blue }; - game.draw_circle_map(center, 3, color, true); - } - } - } - } } diff --git a/protossbot/src/utils/worker_management.rs b/protossbot/src/utils/worker_management.rs index e474221..82d1842 100644 --- a/protossbot/src/utils/worker_management.rs +++ b/protossbot/src/utils/worker_management.rs @@ -45,13 +45,6 @@ fn assign_worker_to_mineral(game: &Game, worker: &Unit, state: &mut GameState) { state.intended_commands.insert(worker_id, intended_cmd); - println!( - "Worker {} current order: {:?}, assigning to mine from mineral at {:?}", - worker_id, - worker.get_order(), - mineral.get_position() - ); - if worker.gather(&mineral).is_ok() { println!( "Assigned worker {} to mine from mineral at {:?}", diff --git a/protossbot/static/index.html b/protossbot/static/index.html index 2eeefcc..796bec3 100644 --- a/protossbot/static/index.html +++ b/protossbot/static/index.html @@ -31,13 +31,6 @@ width: 100%; } - h1 { - color: #d4af37; - margin-bottom: 30px; - font-size: 28px; - text-align: center; - } - .control-group { margin-bottom: 30px; } @@ -60,13 +53,17 @@ font-weight: bold; color: #d4af37; display: block; + transition: color 0.3s; + } + .speed-fast { + color: #4ade80; /* green */ + } + .speed-slow { + color: #f87171; /* red */ } .speed-label { - color: #999; - font-size: 12px; - text-transform: uppercase; - letter-spacing: 1px; + display: none; /* hide label for compact view */ } .preset-buttons { @@ -87,6 +84,10 @@ background: #2d2d2d; color: #d4af37; } + button.active { + background: #d4af37; + color: #1e1e1e; + } button small { display: block; @@ -201,27 +202,28 @@
-

⚡ Game Speed Control

-
- 42 - Current Speed -
-
- - - - + + + +
-

📋 Build Status

Loading...
    @@ -232,46 +234,52 @@