runs, but not webserver

This commit is contained in:
2026-01-22 17:57:52 -07:00
parent feda48b0a4
commit ce7ce427c6
5 changed files with 91 additions and 40 deletions

View File

@@ -14,6 +14,11 @@ tokio-tungstenite = "0.24"
futures-util = "0.3" futures-util = "0.3"
tower-http = { version = "0.6", features = ["fs", "cors"] } tower-http = { version = "0.6", features = ["fs", "cors"] }
rand = "0.8" rand = "0.8"
leptos = { version = "0.6", features = ["csr"] }
leptos_axum = { version = "0.6" }
leptos_meta = { version = "0.6" }
leptos_router = { version = "0.6" }
protoss-bot-web = { path = "web" }
[profile.release] [profile.release]
opt-level = 3 opt-level = 3

View File

@@ -1,6 +1,9 @@
use crate::{
state::game_state::GameState,
utils::{build_manager, worker_management},
};
use rsbwapi::*; use rsbwapi::*;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use crate::{state::game_state::GameState, utils::{build_manager, worker_management}};
fn draw_unit_ids(game: &Game) { fn draw_unit_ids(game: &Game) {
for unit in game.get_all_units() { for unit in game.get_all_units() {
@@ -47,6 +50,17 @@ impl AiModule for ProtosBot {
return; return;
} }
println!("unit created: {:?}", unit.get_type()); println!("unit created: {:?}", unit.get_type());
// Check if the created unit is a building
if !unit.get_type().is_building() {
return;
}
let Ok(mut locked_state) = self.game_state.lock() else {
return;
};
build_manager::on_building_create(&unit, &mut locked_state);
} }
fn on_unit_morph(&mut self, _game: &Game, _unit: Unit) {} fn on_unit_morph(&mut self, _game: &Game, _unit: Unit) {}

View File

@@ -3,14 +3,41 @@ mod state;
mod utils; mod utils;
use bot::ProtosBot; use bot::ProtosBot;
use std::sync::{Arc, Mutex};
use state::game_state::GameState; use state::game_state::GameState;
use std::sync::{Arc, Mutex};
fn main() { fn main() {
println!("Starting RustBot..."); println!("Starting RustBot...");
let game_state = Arc::new(Mutex::new(GameState::default())); let game_state = Arc::new(Mutex::new(GameState::default()));
// // Start the webserver in a separate thread
// std::thread::spawn(|| {
// let rt = tokio::runtime::Runtime::new().unwrap();
// rt.block_on(start_webserver());
// });
rsbwapi::start(move |_game| ProtosBot::new(game_state.clone())); rsbwapi::start(move |_game| ProtosBot::new(game_state.clone()));
} }
async fn start_webserver() {
use axum::Router;
use leptos::*;
use leptos_axum::{generate_route_list, LeptosRoutes};
use protoss_bot_web::App;
let conf = get_configuration(None).await.unwrap();
let leptos_options = conf.leptos_options;
let addr = leptos_options.site_addr;
let routes = generate_route_list(App);
let app = Router::new()
.leptos_routes(&leptos_options, routes, App)
.with_state(leptos_options);
println!("Web server listening on http://{}", &addr);
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
axum::serve(listener, app.into_make_service())
.await
.unwrap();
}

View File

@@ -1,4 +1,4 @@
use rsbwapi::{Game, Order, Player, UnitType}; use rsbwapi::{Game, Order, Player, Unit, UnitType};
use crate::{ use crate::{
state::game_state::{BuildHistoryEntry, GameState, IntendedCommand}, state::game_state::{BuildHistoryEntry, GameState, IntendedCommand},
@@ -10,6 +10,27 @@ pub fn on_frame(game: &Game, player: &Player, state: &mut GameState) {
try_start_next_build(game, player, state); try_start_next_build(game, player, state);
} }
pub fn on_building_create(unit: &Unit, state: &mut GameState) {
// Find the build history entry associated with this building type
// and remove the probe's assignment
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 {
// Remove the probe's intended command (PlaceBuilding order)
state.intended_commands.remove(&probe_id);
println!(
"Building {} started. Removed assignment for probe {}",
unit.get_type().name(),
probe_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) {
let Some(unit_type) = get_next_thing_to_build(game, player, state) else { let Some(unit_type) = get_next_thing_to_build(game, player, state) else {
return; return;
@@ -62,7 +83,8 @@ fn get_next_thing_to_build(game: &Game, player: &Player, state: &GameState) -> O
if unit_type.is_building() { if unit_type.is_building() {
let builder = find_builder_for_unit(player, *unit_type)?; let builder = find_builder_for_unit(player, *unit_type)?;
let build_location = build_location_utils::find_build_location(game, &builder, *unit_type, 20); let build_location =
build_location_utils::find_build_location(game, &builder, *unit_type, 20);
if build_location.is_none() { if build_location.is_none() {
continue; continue;
} }
@@ -71,9 +93,9 @@ fn get_next_thing_to_build(game: &Game, player: &Player, state: &GameState) -> O
candidates.push(*unit_type); candidates.push(*unit_type);
} }
candidates.into_iter().max_by_key(|unit_type| { candidates
unit_type.mineral_price() + unit_type.gas_price() .into_iter()
}) .max_by_key(|unit_type| unit_type.mineral_price() + unit_type.gas_price())
} }
fn check_need_more_supply(game: &Game, player: &Player) -> Option<UnitType> { fn check_need_more_supply(game: &Game, player: &Player) -> Option<UnitType> {
@@ -92,7 +114,8 @@ fn check_need_more_supply(game: &Game, player: &Player) -> Option<UnitType> {
if can_afford_unit(player, pylon_type) { if can_afford_unit(player, pylon_type) {
if let Some(builder) = find_builder_for_unit(player, pylon_type) { if let Some(builder) = find_builder_for_unit(player, pylon_type) {
let build_location = build_location_utils::find_build_location(game, &builder, pylon_type, 20); let build_location =
build_location_utils::find_build_location(game, &builder, pylon_type, 20);
if build_location.is_some() { if build_location.is_some() {
return Some(pylon_type); return Some(pylon_type);
} }
@@ -115,7 +138,12 @@ fn find_builder_for_unit(player: &Player, unit_type: UnitType) -> Option<rsbwapi
.cloned() .cloned()
} }
fn assign_builder_to_construct(game: &Game, builder: &rsbwapi::Unit, unit_type: UnitType, state: &mut GameState) -> bool { fn assign_builder_to_construct(
game: &Game,
builder: &rsbwapi::Unit,
unit_type: UnitType,
state: &mut GameState,
) -> bool {
let builder_id = builder.get_id(); let builder_id = builder.get_id();
if unit_type.is_building() { if unit_type.is_building() {
@@ -180,8 +208,6 @@ fn count_units_of_type(player: &Player, _state: &GameState, unit_type: UnitType)
.filter(|u| u.get_type() == unit_type) .filter(|u| u.get_type() == unit_type)
.count() as i32; .count() as i32;
existing existing
} }

View File

@@ -10,33 +10,12 @@ pub fn assign_idle_workers_to_minerals(game: &Game, player: &Player, state: &mut
.cloned() .cloned()
.collect(); .collect();
// First, clean up workers that finished building // Assign idle workers to mining
reassign_finished_builders(game, &workers, state);
// Then assign idle workers to mining
for worker in workers { for worker in workers {
assign_worker_to_mineral(game, &worker, state); assign_worker_to_mineral(game, &worker, state);
} }
} }
fn reassign_finished_builders(_game: &Game, workers: &[Unit], state: &mut GameState) {
for worker in workers {
let worker_id = worker.get_id();
if let Some(cmd) = state.intended_commands.get(&worker_id) {
if cmd.order == Order::PlaceBuilding {
let current_order = worker.get_order();
if worker.is_idle() && current_order != Order::PlaceBuilding && current_order != Order::ConstructingBuilding {
println!("Worker {} with order {:?} finished building, reassigning to minerals", worker_id, current_order);
state.intended_commands.remove(&worker_id);
}
} else if cmd.order == Order::Train && worker.is_idle() && !worker.is_training() {
state.intended_commands.remove(&worker_id);
}
}
}
}
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();