use std::sync::Arc; use tracing::{warn, info, trace}; use tokio::sync::{Mutex, OwnedMutexGuard, Semaphore, OwnedSemaphorePermit}; use crate::{blocks::Position, turtle::TurtleCommander}; use crate::turtle::{TurtleCommand::*, TurtleCommandResponse}; /// List of available depots /// /// below the specified position is an output chest of infinite capacity /// ahead of the specified position is a chest of combustibles #[derive(Clone, Debug)] pub struct Depots { depots: Arc>>>>, depot_semaphore: Arc, } pub struct DepotGuard { mutex: OwnedMutexGuard, #[allow(unused)] semaphore: OwnedSemaphorePermit, // "dropped in declaration order" // - reference chapter 10.8 } impl DepotGuard { pub fn new(mutex: OwnedMutexGuard, semaphore: OwnedSemaphorePermit) -> Self { Self { mutex, semaphore } } fn position(&self) -> &Position { &self.mutex } } impl Depots { /// Nearest depot to the given position pub async fn nearest(&self, pos: Position) -> DepotGuard { let permit = self.depot_semaphore.clone().acquire_owned().await.unwrap(); let mutex = self.depots.lock().await .iter().map(|i| i.clone()) .filter_map(|i| i.try_lock_owned().ok()) .min_by_key(|d| d.manhattan(pos)) .map(|d| d); DepotGuard::new(mutex.unwrap(), permit) } pub async fn dock(&self, turtle: TurtleCommander) -> Option { let depot = self.clone().nearest(turtle.pos().await).await; trace!("depot at {:?}", depot.position()); turtle.goto(*depot.position()).await?; // dump inventory for i in 1..=16 { turtle.execute(Select(i)).await; turtle.execute(DropDown(64)).await; } // refuel turtle.execute(Select(1)).await; let limit = turtle.fuel_limit(); while turtle.fuel() + 1000 < limit { turtle.execute(SuckFront(64)).await; let re = turtle.execute(Refuel).await; turtle.execute(DropDown(64)).await; if let TurtleCommandResponse::Failure = re.ret { // partial refuel, good enough warn!("only received {} fuel", turtle.fuel()); if turtle.fuel() > 1500 { break; } else { turtle.execute(Wait(15)).await; } } } // This can fail, we don't really care (as long as it executes once) turtle.execute(Backward(4)).await; drop(depot); // lava bucket fix for i in 1..=16 { turtle.execute(Select(i)).await; turtle.execute(DropDown(64)).await; } Some(turtle.fuel()) } pub async fn add(&self, pos: Position) { info!("new depot at {pos:?}"); self.depots.lock().await.push(Arc::new(Mutex::new(pos))); self.depot_semaphore.add_permits(1); } pub fn from_vec(vec: Vec) -> Self { let mut depots = Vec::new(); for depot in vec { depots.push(Arc::new(Mutex::new(depot))); } let permits = depots.len(); Depots { depots: Arc::new(Mutex::new(depots)), depot_semaphore: Arc::new(Semaphore::new(permits)) } } pub async fn to_vec(self) -> Vec { let mut depots = Vec::new(); for depot in self.depots.lock().await.iter() { depots.push(*depot.lock().await) } depots } }