From 419b816ea7fadfff5cfa28c0cfa71f7c139a3e25 Mon Sep 17 00:00:00 2001 From: InfinityDevTech <47366052+InfinityDevTech@users.noreply.github.com> Date: Sun, 20 Aug 2023 21:38:50 -0400 Subject: [PATCH] Before test --- Cargo.lock | 68 ---------------- Cargo.toml | 6 +- javascript/main.js | 124 ++++++++++++++++++----------- src/building/mod.rs | 1 + src/building/roads.rs | 47 +++++++++++ src/lib.rs | 150 +++++++++++++++++++++-------------- src/memory.rs | 85 ++++++++++++++++---- src/movement/creep.rs | 88 ++++++++++++++++++++ src/movement/mod.rs | 2 + src/movement/move_target.rs | 98 +++++++++++++++++++++++ src/roles/local/builder.rs | 20 +++++ src/roles/local/harvester.rs | 15 ++++ src/roles/local/mod.rs | 3 + src/roles/local/upgrader.rs | 32 ++++++++ src/roles/mod.rs | 1 + src/room/local.rs | 20 +++++ src/room/mod.rs | 1 + 17 files changed, 564 insertions(+), 197 deletions(-) create mode 100644 src/building/mod.rs create mode 100644 src/building/roads.rs create mode 100644 src/movement/creep.rs create mode 100644 src/movement/mod.rs create mode 100644 src/movement/move_target.rs create mode 100644 src/roles/local/builder.rs create mode 100644 src/roles/local/harvester.rs create mode 100644 src/roles/local/mod.rs create mode 100644 src/roles/local/upgrader.rs create mode 100644 src/roles/mod.rs create mode 100644 src/room/local.rs create mode 100644 src/room/mod.rs diff --git a/Cargo.lock b/Cargo.lock index f2c2bd4..bc16adf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,12 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - [[package]] name = "arrayvec" version = "0.7.4" @@ -20,21 +14,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" -[[package]] -name = "base64" -version = "0.21.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" - -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - [[package]] name = "bumpalo" version = "3.13.0" @@ -57,25 +36,12 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "crc32fast" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" -dependencies = [ - "cfg-if", -] - [[package]] name = "crustacean" version = "0.0.0" dependencies = [ - "base64", - "bincode", "fern", - "flate2", "js-sys", - "lazy_static", "log", "screeps-game-api", "serde", @@ -127,16 +93,6 @@ dependencies = [ "log", ] -[[package]] -name = "flate2" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - [[package]] name = "heck" version = "0.4.1" @@ -158,30 +114,12 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -dependencies = [ - "spin", -] - [[package]] name = "log" version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" -[[package]] -name = "miniz_oxide" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" -dependencies = [ - "adler", -] - [[package]] name = "num-derive" version = "0.4.0" @@ -310,12 +248,6 @@ dependencies = [ "syn", ] -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - [[package]] name = "structstruck" version = "0.4.1" diff --git a/Cargo.toml b/Cargo.toml index c371843..e221013 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,15 +12,11 @@ js-sys = "0.3" log = "0.4" fern = "0.6" screeps-game-api = "0.15" -wasm-bindgen = "0.2" +wasm-bindgen = {version = "0.2", features=[]} web-sys = { version = "0.3", features = ["console"] } structstruck = "0.4.1" serde_json = "1.0.104" serde = { version = "1.0.183", features = ["derive"] } -bincode = "1.3.3" -lazy_static = { version = "1.4.0", features = ["spin"] } -base64 = "0.21.2" -flate2 = "1.0.26" [dev-dependencies] wasm-bindgen-test = "0.3" diff --git a/javascript/main.js b/javascript/main.js index aae2b0f..14f5f3a 100644 --- a/javascript/main.js +++ b/javascript/main.js @@ -3,57 +3,85 @@ let wasm_module; // replace this with the name of your module const MODULE_NAME = "screeps"; +let EXECUTION_PAUSED = false; +let RED_BUTTON = false; function console_error(...args) { - console.log(...args); - Game.notify(args.join(' ')); + console.log(...args); + Game.notify(args.join(" ")); +} + +global.big_red_button = function (input) { + EXECUTION_PAUSED = true; + console.log("The big red button has been pressed. Are you sure you want to do this?"); + console.log("This will suicide EVERY room, and EVERY creep. Are you still sure?"); + console.log("If you are sure. Then rerun this command like big_red_button(\"yes\") or big_red_button(\"no\")"); + + if (input == undefined) { + EXECUTION_PAUSED = true; + return "Suicide? [y/n]" + } else if (input.toLowerCase() == "n" || input.toLowerCase() == "no") { + EXECUTION_PAUSED = false; + return "The bot will live to see another day..." + } else if (input.toLowerCase() == "y" || input.toLowerCase() == "yes") { + RED_BUTTON = true; + return "The bot will suicide on the next tick. Look at what you have done..." + } +}; + +global.toggle_exec = function () { + EXECUTION_PAUSED = !EXECUTION_PAUSED + return `Successfully toggled execution pause to: ${EXECUTION_PAUSED}` } module.exports.loop = function () { - // Replace the Memory object (which gets populated into our global each tick) with an empty - // object, so that accesses to it from within the driver that we can't prevent (such as - // when a creep is spawned) won't trigger an attempt to parse RawMemory. Replace the object - // with one unattached to memory magic - game functions will access the `Memory` object and - // can throw data in here, and it'll go away at the end of tick. - - // Because it's in place, RawMemory's string won't be thrown to JSON.parse to deserialize - - // and because that didn't happen, RawMemory._parsed isn't set and won't trigger a - // post-tick serialize. - //delete global.Memory; - //global.Memory = {}; - try { - if (wasm_module) { - wasm_module.loop(); - } else { - // attempt to load the wasm only if there's enough bucket to do a bunch of work this tick - if (Game.cpu.bucket < 500) { - console.log("Not enough in the CPU bucket, not going to compile - CPU: " + JSON.stringify(Game.cpu)); - return; - } - - // delect the module from the cache, so we can reload it - if (MODULE_NAME in require.cache) { - delete require.cache[MODULE_NAME]; - } - // load the wasm module - wasm_module = require(MODULE_NAME); - if (wasm_module != undefined) { - // load the wasm instance! - wasm_module.initialize_instance(); - // run the setup function, which configures logging - wasm_module.setup(); - // go ahead and run the loop for its first tick - wasm_module.loop(); - } else { - console.log("Wasm module is undefined, is the name correct?") - } - } - } catch (error) { - console_error("Found error: ", error); - if (error.stack) { - console_error("Stack trace: ", error.stack); - } - console_error("Reloading wasm module"); - wasm_module = null; + // Replace the Memory object (which gets populated into our global each tick) with an empty + // object, so that accesses to it from within the driver that we can't prevent (such as + // when a creep is spawned) won't trigger an attempt to parse RawMemory. Replace the object + // with one unattached to memory magic - game functions will access the `Memory` object and + // can throw data in here, and it'll go away at the end of tick. + + // Because it's in place, RawMemory's string won't be thrown to JSON.parse to deserialize - + // and because that didn't happen, RawMemory._parsed isn't set and won't trigger a + // post-tick serialize. + delete global.Memory; + global.Memory = {}; + try { + if (wasm_module) { + if (RED_BUTTON) { + wasm_module.red_button(); + } + if (!EXECUTION_PAUSED) { + wasm_module.loop(); + } + } else { + // attempt to load the wasm only if there's enough bucket to do a bunch of work this tick + if (Game.cpu.bucket < 500) { + console.log("Not enough in the CPU bucket, not going to compile - CPU: " + JSON.stringify(Game.cpu)); + return; + } + + // delect the module from the cache, so we can reload it + if (MODULE_NAME in require.cache) { + delete require.cache[MODULE_NAME]; + } + // load the wasm module + wasm_module = require(MODULE_NAME); + if (wasm_module != undefined) { + // load the wasm instance! + wasm_module.initialize_instance(); + // run the setup function, which configures logging + wasm_module.setup(); + } else { + console.log("Wasm module is undefined, is the name correct?"); + } } -} + } catch (error) { + console_error("Found error: ", error); + if (error.stack) { + console_error("Stack trace: ", error.stack); + } + console_error("Reloading wasm module"); + wasm_module = null; + } +}; diff --git a/src/building/mod.rs b/src/building/mod.rs new file mode 100644 index 0000000..c22d5e1 --- /dev/null +++ b/src/building/mod.rs @@ -0,0 +1 @@ +pub mod roads; \ No newline at end of file diff --git a/src/building/roads.rs b/src/building/roads.rs new file mode 100644 index 0000000..f5c63fc --- /dev/null +++ b/src/building/roads.rs @@ -0,0 +1,47 @@ +use log::info; +use screeps::{Source, StructureSpawn, HasPosition, pathfinder::{SearchOptions, MultiRoomCostResult, self}, RoomName, LocalCostMatrix, game, StructureType, find, StructureProperties, look, ConstructionSite}; + +pub fn source_to_spawn(source: &Source, spawn: &StructureSpawn) { + let opts = SearchOptions::new(road_callback).max_ops(100000000).plain_cost(2).swamp_cost(5).max_rooms(1); + let path = pathfinder::search(spawn.pos(), source.pos(), 1, Some(opts)); + if !path.incomplete() { + info!("Road complete"); + for pos in path.path() { + let room = game::rooms().get(pos.room_name()).unwrap(); + if room.look_for_at_xy(look::CONSTRUCTION_SITES, pos.x().u8(), pos.y().u8()).is_empty() { + match room.create_construction_site(pos.x().u8(), pos.y().u8(), StructureType::Road, None) { + Ok(_) => {}, + Err(e) => { + println!("Error creating construction site: {:?}", e); + } + }; + } + } + } else { + info!("Road incomplete?"); + } +} + +pub fn road_callback(room_name: RoomName) -> MultiRoomCostResult { + let mut matrix = LocalCostMatrix::new(); + if let Some(room) = game::rooms().get(room_name) { + for road in room.find(find::STRUCTURES, None).into_iter().filter(|s| s.structure_type() == StructureType::Road) { + matrix.set(road.pos().xy(), 1); + } + let csites = room.find(find::CONSTRUCTION_SITES, None); + for site in csites { + match site.structure_type() { + StructureType::Road => matrix.set(site.pos().xy(), 1), + StructureType::Rampart => matrix.set(site.pos().xy(), 1), + StructureType::Container => matrix.set(site.pos().xy(), 1), + _ => todo!(), + } + } + for creep in room.find(find::CREEPS, None) { + matrix.set(creep.pos().xy(), 1); + } + } + + MultiRoomCostResult::CostMatrix(matrix.into()) + +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 2a9ced6..4fd2aa0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,10 @@ +use std::collections::HashMap; + use log::*; +use movement::creep; use screeps::ConstructionSite; use screeps::{ - constants::{ErrorCode, Part, ResourceType}, + constants::{Part, ResourceType}, enums::StructureObject, find, game, local::ObjectId, @@ -11,15 +14,19 @@ use screeps::{ use serde::{Deserialize, Serialize}; use wasm_bindgen::prelude::*; -use crate::memory::{CreepMemory, Memory}; +use crate::memory::{CreepMemory, ScreepsMemory}; +mod building; mod logging; mod memory; +mod movement; +mod roles; +mod room; // add wasm_bindgen to any function you would like to expose for call from js #[wasm_bindgen(js_name = setup)] pub fn setup() { - logging::setup_logging(logging::Debug); + logging::setup_logging(logging::Info); } // this enum will represent a creep's lock on a specific target object, storing a js reference @@ -30,55 +37,65 @@ pub enum CreepTarget { Upgrade(ObjectId), Harvest(ObjectId), Build(ObjectId), + Rename(ObjectId) } // to use a reserved name as a function name, use `js_name`: #[wasm_bindgen(js_name = loop)] pub fn game_loop() { debug!("Loop starting! CPU: {}", game::cpu::get_used()); - debug!("Getting memory"); - let mut memory = crate::Memory::init_memory(); - debug!("Running creeps"); - for (name, _) in memory.creeps.clone() { + let mut memory = ScreepsMemory::init_memory(); + + let mut ran_creeps: Vec = Vec::new(); + for name in game::creeps().keys() { let creep = game::creeps().get(name.clone()); if creep.is_none() { - info!("Found non-existent creep, removing..."); + // Remove creep that no longer exists from memory memory.creeps.remove(&name); - memory.write_memory(); } else { - run_creep(&creep.unwrap(), &mut memory.creeps.get_mut(&name).unwrap()); - memory.write_memory(); + match memory.creeps.get_mut(&name) { + Some(creepmem) => { + run_creep(&creep.unwrap(), creepmem); + } + None => { + // This creep is new, add it to memory + memory.create_creep(&name); + } + } + ran_creeps.push(name.clone()); } } - debug!("Running spawns"); + // Remove creeps that have died from memory + memory.creeps.retain(|name, _| ran_creeps.contains(name)); + let mut additional = 0; for spawn in game::spawns().values() { - debug!("Running spawn {}", String::from(spawn.name())); - + // Default body for now, will be sorted out later. let body = [Part::Move, Part::Move, Part::Carry, Part::Work]; if spawn.room().unwrap().energy_available() >= body.iter().map(|p| p.cost()).sum() { - // create a unique name, spawn. - let name_base = game::time(); - let name = format!("{}-{}", name_base, additional); - // note that this bot has a fatal flaw; spawning a creep - // creates Memory.creeps[creep_name] which will build up forever; - // these memory entries should be prevented (todo doc link on how) or cleaned up + let name = format!("{}-{}", game::time(), additional); match spawn.spawn_creep(&body, &name) { - Ok(()) => additional += 1, + Ok(()) => { + additional += 1; + memory.create_creep(&name); + } Err(e) => warn!("Couldn't spawn: {:?}", e), } } } - info!("Done! cpu: {}", game::cpu::get_used()); + + // Bot is finished, write the local copy of memory. + // This should be only executed ONCE per tick, as it is quite expensive. + memory.write_memory(); + + info!("Done! Cpu used: {}", game::cpu::get_used()); } fn run_creep(creep: &Creep, creepmem: &mut CreepMemory) { if creep.spawning() { return; } - let name = creep.name(); - debug!("Running creep {}", name); let target = &creepmem.work; match target { @@ -89,44 +106,24 @@ fn run_creep(creep: &Creep, creepmem: &mut CreepMemory) { if creep.store().get_used_capacity(Some(ResourceType::Energy)) > 0 => { if let Some(controller) = controller_id.resolve() { - creep - .upgrade_controller(&controller) - .unwrap_or_else(|e| match e { - ErrorCode::NotInRange => { - let _ = creep.move_to(&controller); - } - _ => { - warn!("Couldn't upgrade: {:?}", e); - creepmem.work = None; - } - }); + roles::local::upgrader::upgrade(creep, creepmem, controller); } else { - creepmem.work = None; + creepmem.set_work(None); } } CreepTarget::Harvest(source_id) if creep.store().get_free_capacity(Some(ResourceType::Energy)) > 0 => { if let Some(source) = source_id.resolve() { - if creep.pos().is_near_to(source.pos()) { - creep.harvest(&source).unwrap_or_else(|e| { - warn!("couldn't harvest: {:?}", e); - creepmem.work = None; - }); - } else { - let _ = creep.move_to(&source); - } + roles::local::harvester::harvest(creep, creepmem, source); } else { - creepmem.work = None; + creepmem.set_work(None); } } CreepTarget::Build(site_id) => { if let Some(site) = site_id.resolve() { if creep.pos().is_near_to(site.pos()) { - creep.build(&site).unwrap_or_else(|e| { - warn!("couldn't build: {:?}", e); - creepmem.work = None; - }); + roles::local::builder::build(creep, creepmem, site); } else { let _ = creep.move_to(&site); } @@ -134,24 +131,57 @@ fn run_creep(creep: &Creep, creepmem: &mut CreepMemory) { creepmem.work = None; } } + CreepTarget::Rename(controller_id) => { + if let Some(controller) = controller_id.resolve() { + if creep.pos().is_near_to(controller.pos()) { + let _ = creep.sign_controller(&controller, "Ferris FTW!"); + creepmem.work = None; + } else { + creep::move_to(&creep.name(), creepmem, controller.pos()); + } + } else { + creepmem.work = None; + } + } _ => { - creepmem.work = None; + creepmem.set_work(None); } }; } None => { - // no target, let's find one depending on if we have energy - let room = creep.room().expect("couldn't resolve creep room"); + // Should never fail. + let room = creep.room().unwrap(); if creep.store().get_used_capacity(Some(ResourceType::Energy)) > 0 { - for structure in room.find(find::STRUCTURES, None).iter() { - if let StructureObject::StructureController(controller) = structure { - creepmem.work = Some(CreepTarget::Upgrade(controller.id())); - break; - } - } + creepmem.set_work(Some(CreepTarget::Upgrade(room.controller().unwrap().id()))); } else if let Some(source) = room.find(find::SOURCES_ACTIVE, None).get(0) { - creepmem.work = Some(CreepTarget::Harvest(source.id())); + creepmem.set_work(Some(CreepTarget::Harvest(source.id()))); } } } -} \ No newline at end of file +} + +#[wasm_bindgen(js_name = red_button)] +pub fn big_red_button() { + for creep in game::creeps().values() { + let _ = creep.say("GOD WHY IS THIS HAPPENING???", true); + let _ = creep.suicide(); + } + for room in game::rooms().values() { + match room.controller() { + Some(controller) => { + for structure in room.find(find::MY_STRUCTURES, None) { + let _ = structure.destroy(); + } + for csite in room.find(find::MY_CONSTRUCTION_SITES, None) { + let _ = csite.remove(); + } + let _ = controller.unclaim(); + }, + None => {}, + } + } + let mut memory = memory::ScreepsMemory::init_memory(); + memory.creeps = HashMap::new(); + memory.rooms = HashMap::new(); + memory.write_memory(); +} diff --git a/src/memory.rs b/src/memory.rs index 1ffa700..1cb85f4 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -1,7 +1,6 @@ -#![allow(dead_code)] - use std::collections::HashMap; +use log::error; use serde::{Deserialize, Serialize}; use js_sys::JsString; @@ -9,13 +8,12 @@ use js_sys::JsString; structstruck::strike! { #[strikethrough[derive(Serialize, Deserialize, Debug, Clone)]] pub struct CreepMemory{ - pub _move: Option, @@ -25,30 +23,85 @@ pub struct CreepMemory{ structstruck::strike! { #[strikethrough[derive(Serialize, Deserialize, Debug, Clone)]] - pub struct Memory { - pub creeps: HashMap +pub struct RoomMemory{ + room_type: String, + pub sources: HashMap + }>, } } -impl Memory { +structstruck::strike! { + #[strikethrough[derive(Serialize, Deserialize, Debug, Clone)]] + pub struct ScreepsMemory { + pub creeps: HashMap, + pub rooms: HashMap +} +} + +impl ScreepsMemory { pub fn init_memory() -> Self { let memory_jsstring = screeps::raw_memory::get(); let memory_string = memory_jsstring.as_string().unwrap(); if memory_string == "" { - let memory = Memory { + let memory = ScreepsMemory { creeps: HashMap::new(), + rooms: HashMap::new(), }; memory.write_memory(); memory } else { - let memory: Memory = serde_json::from_str(&memory_string).unwrap(); - memory + match serde_json::from_str(&memory_string) { + Ok(memory) => memory, + Err(e) => { + error!("Error parsing memory: {}", e); + error!("This is a critical error, memory MUST be reset to default state."); + ScreepsMemory { + creeps: HashMap::new(), + rooms: HashMap::new(), + } + } + } } } - pub fn write_memory(&self) { - //let serialized = serde_json::to_string(&self).unwrap(); - //et js_serialized = JsString::from(serialized); - //screeps::raw_memory::set(&js_serialized); + let serialized = serde_json::to_string(&self).unwrap(); + let js_serialized = JsString::from(serialized); + screeps::raw_memory::set(&js_serialized); + } + + pub fn create_creep(&mut self, name: &str) { + self.creeps.insert( + name.to_string(), + CreepMemory { + movement: None, + work: None, + }, + ); + } + + pub fn create_room(&mut self, name: &str) { + self.rooms.insert( + name.to_string(), + RoomMemory { + room_type: "local".to_string(), + sources: HashMap::new(), + }, + ); + } +} + +impl CreepMemory { + pub fn set_movement(&mut self, movement: Option) { + self.movement = movement; + } + + pub fn set_work(&mut self, work: Option) { + self.work = work; } } diff --git a/src/movement/creep.rs b/src/movement/creep.rs new file mode 100644 index 0000000..f2f6442 --- /dev/null +++ b/src/movement/creep.rs @@ -0,0 +1,88 @@ +use log::info; +use screeps::{game, Direction, HasPosition, Creep, RoomPosition, Position}; + +use crate::memory::{Movement, CreepMemory}; + +use super::move_target::MoveTarget; + +pub fn move_to(creep_name: &String, creep_memory: &mut CreepMemory, target: Position) { + let creep = game::creeps().get(creep_name.to_string()).unwrap(); + match &creep_memory.movement { + Some(path) => { + move_by_path(creep_name.to_string(), path.clone(), creep_memory) + } + None => { + let target = MoveTarget { + pos: target, + range: 1, + }.find_path_to(creep.pos()); + creep_memory.movement = Some(target.clone()); + move_by_path(creep_name.to_string(), target, creep_memory); + } + + } +} + +pub fn move_by_path(creep_name: String, path: Movement, memory: &mut CreepMemory) { + let creep = game::creeps().get(creep_name).unwrap(); + + if creep.fatigue() > 0 { + return; + } + let serialized_path = path.path; + let serialized_vec = serialized_path.split("").filter(|x| x != &"").map(|x| x.parse::().unwrap()).collect::>(); + let step_dir = num_to_dir(serialized_vec[0]); + + match creep.move_direction(step_dir) { + Ok(_) => {}, + Err(e) => info!("Creep move failed, {:?}", e), + }; + + let serialized_vec = serialized_vec[1..].to_vec(); + let serialized_path = serialized_vec.iter().map(|x| x.to_string()).collect::>().join(""); + if serialized_vec.len() == 0 { + memory.set_movement(None); + } else { + memory.set_movement(Some(Movement { + dest: path.dest, + path: serialized_path, + room: path.room, + })); + } + + let mut points = vec![]; + let mut cursor = (creep.pos().x().u8() as f32, creep.pos().y().u8() as f32); + for step in serialized_vec { + let dir = num_to_dir(step); + let (x, y) = dir_to_coords(dir, cursor.0, cursor.1); + points.push((x as f32, y as f32)); + cursor = (x, y); + } +} + +pub fn num_to_dir(num: u8) -> Direction { + match num { + 1 => Direction::Top, + 2 => Direction::TopRight, + 3 => Direction::Right, + 4 => Direction::BottomRight, + 5 => Direction::Bottom, + 6 => Direction::BottomLeft, + 7 => Direction::Left, + 8 => Direction::TopLeft, + _ => Direction::Top, + } +} + +pub fn dir_to_coords(dir: Direction, x: f32, y: f32) -> (f32, f32) { + match dir { + Direction::Top => (x, y - 1 as f32), + Direction::TopRight => (x + 1 as f32, y - 1 as f32), + Direction::Right => (x + 1 as f32, y), + Direction::BottomRight => (x + 1 as f32, y + 1 as f32), + Direction::Bottom => (x, y + 1 as f32), + Direction::BottomLeft => (x - 1 as f32, y + 1 as f32), + Direction::Left => (x - 1 as f32, y), + Direction::TopLeft => (x - 1 as f32, y - 1 as f32), + } +} diff --git a/src/movement/mod.rs b/src/movement/mod.rs new file mode 100644 index 0000000..920f6a8 --- /dev/null +++ b/src/movement/mod.rs @@ -0,0 +1,2 @@ +pub mod creep; +pub mod move_target; \ No newline at end of file diff --git a/src/movement/move_target.rs b/src/movement/move_target.rs new file mode 100644 index 0000000..0ab0fa5 --- /dev/null +++ b/src/movement/move_target.rs @@ -0,0 +1,98 @@ +use log::warn; +use screeps::{ + pathfinder::{self, MultiRoomCostResult, SearchOptions}, + Direction, HasPosition, LocalCostMatrix, OwnedStructureProperties, Position, + RoomName, StructureObject, find +}; + +use crate::memory; + +pub struct MoveTarget { + pub pos: Position, + pub range: u32 +} + +impl MoveTarget { + pub fn find_path_to(&mut self, from: Position) -> memory::Movement { + let opts = SearchOptions::new(path_call) + .plain_cost(1) + .swamp_cost(1) + .max_rooms(1) + .max_ops(100000); + let search = pathfinder::search(from, self.pos, self.range, Some(opts)); + + if search.incomplete() { + warn!( + "Incomplete pathfinding search {} {} {}", + search.ops(), + search.cost(), + self.pos + ); + } + + let mut cur_pos = from; + let positions = search.path(); + let mut steps = Vec::with_capacity(positions.len()); + for pos in positions { + if pos.room_name() == cur_pos.room_name() { + match pos.get_direction_to(cur_pos) { + Some(dir) => { + steps.push(Direction::from(-dir)); + } + None => { + warn!("Couldn't get direction to {:?} from {:?}", pos, cur_pos); + break; + } + } + } + cur_pos = pos; + } + let mut steps_string = "".to_string(); + for dirint in steps { + let int = dirint as u8; + let intstring = int.to_string(); + steps_string = steps_string + &intstring; + } + memory::Movement { + dest: memory::Dest { + x: self.pos.x().into(), + y: self.pos.y().into(), + room: self.pos.room_name().to_string(), + }, + path: steps_string, + room: self.pos.room_name().to_string(), + } + } +} + +pub fn path_call(room_name: RoomName) -> MultiRoomCostResult { + let mut matrix = LocalCostMatrix::new(); + if let Some(room) = screeps::game::rooms().get(room_name) { + let objects = room.find(find::STRUCTURES, None); + let creeps = room.find(find::CREEPS, None); + for structure in objects { + let pos = structure.pos(); + match structure { + StructureObject::StructureContainer(_) => matrix.set(pos.xy(), 1), + StructureObject::StructureRampart(rampart) => { + if rampart.my() { + matrix.set(pos.xy(), 1); + } else { + matrix.set(pos.xy(), 255); + } + } + StructureObject::StructureRoad(_) => matrix.set(pos.xy(), 1), + StructureObject::StructureWall(_) => matrix.set(pos.xy(), 255), + _ => { + matrix.set(pos.xy(), 255); + } + } + } + + for creep in creeps { + let pos = creep.pos(); + matrix.set(pos.xy(), 255); + } + } + MultiRoomCostResult::CostMatrix(matrix.into()) +} diff --git a/src/roles/local/builder.rs b/src/roles/local/builder.rs new file mode 100644 index 0000000..bcdb56e --- /dev/null +++ b/src/roles/local/builder.rs @@ -0,0 +1,20 @@ +use log::warn; +use screeps::{ConstructionSite, Creep, HasPosition, SharedCreepProperties}; + +use crate::{memory::CreepMemory, movement}; + +pub fn build(creep: &Creep, creepmem: &mut CreepMemory, site: ConstructionSite) { + if creep.pos().is_near_to(site.pos()) { + creep.build(&site).unwrap_or_else(|e| { + warn!("couldn't build: {:?}", e); + creepmem.work = None; + }); + } else { + let movet = movement::move_target::MoveTarget { + pos: site.pos(), + range: 3 + }.find_path_to(creep.pos()); + creepmem.movement = Some(movet.clone()); + movement::creep::move_by_path(creep.name(), movet, creepmem); + } +} diff --git a/src/roles/local/harvester.rs b/src/roles/local/harvester.rs new file mode 100644 index 0000000..a35b375 --- /dev/null +++ b/src/roles/local/harvester.rs @@ -0,0 +1,15 @@ +use log::warn; +use screeps::{Source, HasPosition, Creep, SharedCreepProperties}; + +use crate::{memory::CreepMemory, movement}; + +pub fn harvest(creep: &Creep, creepmem: &mut CreepMemory, source: Source) { + let name = creep.name(); + if creep.pos().is_near_to(source.pos()) { + creep.harvest(&source).unwrap_or_else(|e| { + warn!("couldn't harvest: {:?}", e); + }); + } else { + movement::creep::move_to(&name, creepmem, source.pos()) + } +} \ No newline at end of file diff --git a/src/roles/local/mod.rs b/src/roles/local/mod.rs new file mode 100644 index 0000000..f5828ac --- /dev/null +++ b/src/roles/local/mod.rs @@ -0,0 +1,3 @@ +pub mod builder; +pub mod harvester; +pub mod upgrader; \ No newline at end of file diff --git a/src/roles/local/upgrader.rs b/src/roles/local/upgrader.rs new file mode 100644 index 0000000..e575ba7 --- /dev/null +++ b/src/roles/local/upgrader.rs @@ -0,0 +1,32 @@ +use log::warn; +use screeps::{Creep, ErrorCode, SharedCreepProperties, StructureController, HasPosition}; + +use crate::memory::CreepMemory; + +pub fn upgrade(creep: &Creep, creepmem: &mut CreepMemory, controller: StructureController) { + let name = creep.name(); + creep + .upgrade_controller(&controller) + .unwrap_or_else(|e| match e { + ErrorCode::NotInRange => { + match &creepmem.movement { + Some(path) => { + crate::movement::creep::move_by_path(name.clone(), path.clone(), creepmem); + } + None => { + let path = crate::movement::move_target::MoveTarget { + pos: controller.pos(), + range: 1 + } + .find_path_to(creep.pos()); + creepmem.movement = Some(path.clone()); + crate::movement::creep::move_by_path(name.clone(), path, creepmem); + } + }; + } + _ => { + let _ = creep.say("🚧", false); + warn!("Error upgrading controller: {:?}", e); + } + }); +} diff --git a/src/roles/mod.rs b/src/roles/mod.rs new file mode 100644 index 0000000..178b08d --- /dev/null +++ b/src/roles/mod.rs @@ -0,0 +1 @@ +pub mod local; \ No newline at end of file diff --git a/src/room/local.rs b/src/room/local.rs new file mode 100644 index 0000000..e194461 --- /dev/null +++ b/src/room/local.rs @@ -0,0 +1,20 @@ +use screeps::{Room, find, HasPosition}; + +use crate::memory::{RoomMemory, self}; + +pub fn run_rom(room: Room, roommem: RoomMemory) { + let controller = room.controller().unwrap(); + match controller.sign() { + Some(sign) => { + if sign.text() != "Ferris FTW!" { + match controller.pos().find_closest_by_range(find::MY_CREEPS) { + Some(creep) => { + + }, + None => {} + } + } + }, + None => todo!(), + } +} \ No newline at end of file diff --git a/src/room/mod.rs b/src/room/mod.rs new file mode 100644 index 0000000..178b08d --- /dev/null +++ b/src/room/mod.rs @@ -0,0 +1 @@ +pub mod local; \ No newline at end of file