diff --git a/client/client.lua b/client/client.lua index e001fff..a0af2f9 100644 --- a/client/client.lua +++ b/client/client.lua @@ -70,6 +70,8 @@ if not idfile then local fuel = turtle.getFuelLevel() if fs.exists("/disk/pos") then io.input("/disk/pos") + else + io.input(io.stdin) end local startpos = io.input() print("Direction (North, South, East, West):") diff --git a/server/src/main.rs b/server/src/main.rs index 70949a6..03d4be7 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -23,7 +23,7 @@ use tokio::sync::{ Mutex, RwLock, mpsc }; use tower::Service; -use turtle::{TurtleTask, Iota, Receiver, Sender, Turtle, TurtleUpdate, TurtleInfo, goto, TurtleCommand}; +use turtle::{TurtleTask, Iota, Receiver, Sender, Turtle, TurtleUpdate, TurtleInfo, TurtleCommand, TurtleCommander}; use crate::{blocks::Block, paths::route}; @@ -42,23 +42,26 @@ struct SavedState { } struct LiveState { - turtles: Vec, + turtles: Vec>>, tasks: Vec>, world: blocks::World, } impl LiveState { - async fn to_save(self) -> SavedState { - SavedState { turtles: self.turtles, world: self.world.to_tree().await } - } - async fn save(&self) -> SavedState { - let turtles = self.turtles.iter().map(|t| t.info()); - SavedState { turtles: turtles.collect(), world: self.world.tree().await } + let mut turtles = Vec::new(); + for turtle in self.turtles.iter() { + turtles.push(turtle.read().await.info()); + }; + SavedState { turtles, world: self.world.tree().await } } fn from_save(save: SavedState) -> Self { - Self { turtles: save.turtles, tasks: Vec::new(), world: World::from_tree(save.world) } + Self { turtles: save.turtles.into_iter().map(|t| Arc::new(RwLock::new(t))).collect(), tasks: Vec::new(), world: World::from_tree(save.world) } + } + + async fn get_turtle(&self, name: u32) -> Option { + TurtleCommander::new(Name::from_num(name), self).await } } @@ -128,7 +131,10 @@ async fn create_turtle( let state = &mut state.write().await; let id = state.turtles.len() as u32; let (send, receive) = mpsc::channel(1); - state.turtles.push(turtle::Turtle::with_channel(id, Position::new(req.position, req.facing), req.fuel, send,receive)); + state.turtles.push( + Arc::new(RwLock::new( + turtle::Turtle::with_channel(id, Position::new(req.position, req.facing), req.fuel, send,receive) + ))); state.tasks.push(VecDeque::new()); @@ -145,23 +151,35 @@ async fn place_up( Path(id): Path, State(state): State, ) -> Json { - let turtle = state.read().await.turtles.get(id as usize).unwrap() - .cmd(); + let turtle = state.read().await.get_turtle(id).await.unwrap(); let response = turtle.execute(turtle::TurtleCommand::PlaceUp).await; Json(response) } +async fn dig( + Path(id): Path, + State(state): State, + Json(req): Json, +) -> &'static str { + let turtle = state.read().await.get_turtle(id).await.unwrap(); + tokio::spawn( + async move { + turtle.goto_adjacent(req.pos).await; + turtle.execute(TurtleCommand::Dig).await + } + ); + + "ACK" +} + async fn set_goal( Path(id): Path, State(state): State, Json(req): Json, ) -> &'static str { - let state = state.read().await; - let turtle = state.turtles[id as usize].cmd(); - let info = turtle.execute(TurtleCommand::Wait(0)).await; - - tokio::spawn(goto(turtle.clone(), info, req, state.world.clone())); + let turtle = state.read().await.get_turtle(id).await.unwrap(); + tokio::spawn(async move {turtle.goto(req).await}); "ACK" } @@ -176,12 +194,10 @@ async fn cancel( } async fn update_turtles(State(state): State) -> &'static str { - state - .write() - .await - .turtles - .iter_mut() - .for_each(|t| t.pending_update = true); + for turtle in state .write().await.turtles.iter_mut() { + turtle.write().await.pending_update = true; + } + "ACK" } @@ -190,7 +206,7 @@ async fn turtle_info( State(state): State, ) -> Json { let state = &mut state.read().await; - let turtle = &state.turtles[id as usize]; + let turtle = &state.turtles[id as usize].read().await; let cloned = Turtle::new( turtle.name.to_num(), @@ -215,7 +231,8 @@ async fn command( } Json( - turtle::process_turtle_update(id, &mut state, req).await.unwrap_or(turtle::TurtleCommand::Update), + turtle::process_turtle_update(id, &mut state, req).await + .unwrap_or(turtle::TurtleCommand::Update), ) } diff --git a/server/src/paths.rs b/server/src/paths.rs index b2e7cfe..ed09db1 100644 --- a/server/src/paths.rs +++ b/server/src/paths.rs @@ -5,12 +5,12 @@ use crate::{ }; use pathfinding::prelude::astar; -pub async fn route_facing(from: Position, to: Position, world: &World) -> Option> { +pub async fn route_facing(from: Position, to: Vec3, world: &World) -> Option> { let facing = |p: &Position| { let ahead = p.dir.unit() + p.pos; - to.pos == ahead + to == ahead }; - route_to(from, to.pos, facing, world).await + route_to(from, to, facing, world).await } pub async fn route(from: Position, to: Position, world: &World) -> Option> { diff --git a/server/src/turtle.rs b/server/src/turtle.rs index d955427..92daa1f 100644 --- a/server/src/turtle.rs +++ b/server/src/turtle.rs @@ -1,4 +1,5 @@ +use crate::SharedControl; use crate::blocks::Block; use crate::blocks::Direction; use crate::blocks::Position; @@ -6,6 +7,7 @@ use crate::blocks::Vec3; use crate::blocks::World; use crate::blocks::nearest; use crate::mine::TurtleMineJob; +use crate::paths::route_facing; use anyhow::Ok; @@ -124,19 +126,25 @@ impl Turtle { } } - - pub fn cmd(&self) -> TurtleCommander { - TurtleCommander { sender: self.sender.as_ref().unwrap().clone() } - } - } #[derive(Clone)] pub struct TurtleCommander { sender: Arc, + world: World, + turtle: Arc>, } impl TurtleCommander { + pub async fn new(turtle: Name, state: &LiveState) -> Option { + let turtle = state.turtles.get(turtle.to_num() as usize)?.clone(); + Some(TurtleCommander { + sender:turtle.clone().read().await.sender.as_ref().unwrap().clone(), + world: state.world.clone(), + turtle, + }) + } + pub async fn execute(&self, command: TurtleCommand) -> TurtleInfo { let (send, recv) = oneshot::channel::(); @@ -144,43 +152,85 @@ impl TurtleCommander { recv.await.unwrap() } -} -pub async fn goto(cmd: TurtleCommander, recent: TurtleInfo, pos: Position, world: World) -> Option<()> { - let mut recent = recent.pos; - loop { - if recent == pos { - break; - } + pub async fn pos(&self) -> Position { + self.turtle.read().await.position + } - let route = route(recent, pos, &world).await?; + pub async fn fuel(&self) -> usize { + self.turtle.read().await.fuel + } - let steps: Vec = route.iter().map_windows(|[from,to]| from.difference(**to).unwrap()).collect(); + pub async fn world(&self) -> World { + self.world.clone() + } - 'route: for (next_position, command) in route.into_iter().skip(1).zip(steps) { - // reroute if the goal point is not empty before moving - // valid routes will explicitly tell you to break ground - - if world.occupied(next_position.pos).await { - break 'route; + pub async fn goto(&self, pos: Position) -> Option<()> { + let mut recent = self.pos().await; + let world = self.world.clone(); + loop { + if recent == pos { + break; } - let state = cmd.execute(command).await; - recent = state.pos; + let route = route(recent, pos, &world).await?; + + let steps: Vec = route.iter().map_windows(|[from,to]| from.difference(**to).unwrap()).collect(); + + 'route: for (next_position, command) in route.into_iter().skip(1).zip(steps) { + // reroute if the goal point is not empty before moving + // valid routes will explicitly tell you to break ground + + if world.occupied(next_position.pos).await { + break 'route; + } + + let state = self.execute(command).await; + recent = state.pos; + } } + Some(()) + } + + pub async fn goto_adjacent(&self, pos: Vec3) -> Option { + let mut recent = self.pos().await; + let world = self.world.clone(); + loop { + + if pos == recent.dir.unit() + recent.pos { + break; + } + + let route = route_facing(recent, pos, &world).await?; + + let steps: Vec = route.iter().map_windows(|[from,to]| from.difference(**to).unwrap()).collect(); + + 'route: for (next_position, command) in route.into_iter().skip(1).zip(steps) { + // reroute if the goal point is not empty before moving + // valid routes will explicitly tell you to break ground + + if world.occupied(next_position.pos).await { + break 'route; + } + + let state = self.execute(command).await; + recent = state.pos; + } + } + Some(recent) } - Some(()) } + pub(crate) async fn process_turtle_update( id: u32, state: &mut LiveState, update: TurtleUpdate, ) -> anyhow::Result { - let turtle = state + let mut turtle = state .turtles .get_mut(id as usize) - .context("nonexisting turtle")?; + .context("nonexisting turtle")?.write().await; let tasks = state .tasks .get_mut(id as usize) @@ -193,9 +243,14 @@ pub(crate) async fn process_turtle_update( } if turtle.fuel != update.fuel { + let diff = turtle.fuel - update.fuel; turtle.fuel = update.fuel; - turtle.position.pos += turtle.queued_movement; + if diff > 0 { + let delta = turtle.queued_movement * diff as i32; + + turtle.position.pos += delta; + } turtle.queued_movement = Vec3::zeros(); } @@ -232,6 +287,7 @@ pub(crate) async fn process_turtle_update( TurtleCommand::Right => turtle.position.dir = turtle.position.dir.right(), _ => {} } + turtle.queued_movement = cmd.unit(turtle.position.dir); return Ok(cmd); } } @@ -293,6 +349,17 @@ impl TurtleCommand { _ => Vec3::zeros(), } } + + pub(crate) fn unit(&self, direction: Direction) -> Vec3 { + let dir = direction.unit(); + match self { + TurtleCommand::Forward(_) => dir, + TurtleCommand::Backward(_) => -dir, + TurtleCommand::Up(_) => Vec3::y(), + TurtleCommand::Down(_) => -Vec3::y(), + _ => Vec3::zeros(), + } + } } #[derive(Serialize, Deserialize, Debug)]