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"
tower-http = { version = "0.6", features = ["fs", "cors"] }
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]
opt-level = 3

View File

@@ -1,6 +1,9 @@
use crate::{
state::game_state::GameState,
utils::{build_manager, worker_management},
};
use rsbwapi::*;
use std::sync::{Arc, Mutex};
use crate::{state::game_state::GameState, utils::{build_manager, worker_management}};
fn draw_unit_ids(game: &Game) {
for unit in game.get_all_units() {
@@ -47,6 +50,17 @@ impl AiModule for ProtosBot {
return;
}
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) {}

View File

@@ -3,14 +3,41 @@ mod state;
mod utils;
use bot::ProtosBot;
use std::sync::{Arc, Mutex};
use state::game_state::GameState;
use std::sync::{Arc, Mutex};
fn main() {
println!("Starting RustBot...");
let game_state = Arc::new(Mutex::new(GameState::default()));
rsbwapi::start(move |_game| ProtosBot::new(game_state.clone() ));
// // 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()));
}
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::{
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);
}
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) {
let Some(unit_type) = get_next_thing_to_build(game, player, state) else {
return;
@@ -62,7 +83,8 @@ fn get_next_thing_to_build(game: &Game, player: &Player, state: &GameState) -> O
if unit_type.is_building() {
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() {
continue;
}
@@ -71,28 +93,29 @@ fn get_next_thing_to_build(game: &Game, player: &Player, state: &GameState) -> O
candidates.push(*unit_type);
}
candidates.into_iter().max_by_key(|unit_type| {
unit_type.mineral_price() + unit_type.gas_price()
})
candidates
.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> {
let supply_used = player.supply_used();
let supply_total = player.supply_total();
if supply_total == 0 {
return None;
}
let supply_remaining = supply_total - supply_used;
let threshold = ((supply_total as f32) * 0.15).ceil() as i32;
if supply_remaining <= threshold && supply_total < 400 {
let pylon_type = UnitType::Protoss_Pylon;
if can_afford_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() {
return Some(pylon_type);
}
@@ -115,9 +138,14 @@ fn find_builder_for_unit(player: &Player, unit_type: UnitType) -> Option<rsbwapi
.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();
if unit_type.is_building() {
let build_location = build_location_utils::find_build_location(game, builder, unit_type, 20);
@@ -129,7 +157,7 @@ fn assign_builder_to_construct(game: &Game, builder: &rsbwapi::Unit, unit_type:
builder_id,
builder.get_position()
);
match builder.build(unit_type, pos) {
Ok(_) => {
println!("Build command succeeded for {}", unit_type.name());
@@ -180,8 +208,6 @@ fn count_units_of_type(player: &Player, _state: &GameState, unit_type: UnitType)
.filter(|u| u.get_type() == unit_type)
.count() as i32;
existing
}

View File

@@ -10,33 +10,12 @@ pub fn assign_idle_workers_to_minerals(game: &Game, player: &Player, state: &mut
.cloned()
.collect();
// First, clean up workers that finished building
reassign_finished_builders(game, &workers, state);
// Then assign idle workers to mining
// Assign idle workers to mining
for worker in workers {
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) {
let worker_id = worker.get_id();