world interior mutability
This commit is contained in:
parent
715520801d
commit
9be02c161d
4 changed files with 197 additions and 167 deletions
|
@ -1,8 +1,43 @@
|
|||
use std::{sync::Arc, ops::Index};
|
||||
|
||||
use nalgebra::Vector3;
|
||||
use rstar::{self, PointDistance, RTree, RTreeObject, AABB};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::sync::{RwLock, RwLockReadGuard, OwnedRwLockReadGuard};
|
||||
|
||||
pub type World = RTree<Block>;
|
||||
use crate::turtle::TurtleCommand;
|
||||
|
||||
pub type WorldReadLock = OwnedRwLockReadGuard<RTree<Block>>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct World {
|
||||
state: Arc<RwLock<RTree<Block>>>, // interior mutability to get around the
|
||||
// questionable architecture of this project
|
||||
}
|
||||
|
||||
impl World {
|
||||
pub fn new() -> Self { Self { state: Arc::new(RwLock::new(RTree::new())) } }
|
||||
pub fn from_tree(tree: RTree<Block>) -> Self { Self { state: Arc::new(RwLock::new(tree)) } }
|
||||
pub async fn to_tree(self) -> RTree<Block> { self.state.write().await.to_owned() }
|
||||
pub async fn tree(&self) -> RTree<Block> { self.state.read().await.clone() }
|
||||
|
||||
pub async fn get(&self, block: Vec3) -> Option<Block> {
|
||||
self.state.read().await.locate_at_point(&block.into()).map(|b| b.to_owned())
|
||||
}
|
||||
|
||||
pub async fn set(&self, block: Block) {
|
||||
self.state.write().await.remove_at_point(&block.pos.into());
|
||||
self.state.write().await.insert(block);
|
||||
}
|
||||
|
||||
pub async fn occupied(&self, block: Vec3) -> bool {
|
||||
self.state.read().await.locate_at_point(&block.into()).is_some_and(|b| b.name != "minecraft:air")
|
||||
}
|
||||
|
||||
pub async fn lock(self) -> WorldReadLock {
|
||||
self.state.read_owned().await
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct Block {
|
||||
|
@ -25,7 +60,46 @@ impl PointDistance for Block {
|
|||
}
|
||||
|
||||
pub type Vec3 = Vector3<i32>;
|
||||
pub type Position = (Vec3, Direction);
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
||||
pub struct Position {
|
||||
pub pos: Vec3,
|
||||
pub dir: Direction,
|
||||
}
|
||||
|
||||
impl Position {
|
||||
pub fn new(pos: Vec3, dir: Direction) -> Self { Self { pos, dir } }
|
||||
|
||||
/// Get a turtle command to map two adjacent positions
|
||||
pub fn difference(self, to: Position) -> Option<TurtleCommand> {
|
||||
use crate::turtle::TurtleCommand::*;
|
||||
|
||||
if self.pos == to.pos {
|
||||
if to.dir == self.dir.left() {
|
||||
Some(Left)
|
||||
} else if to.dir == self.dir.right() {
|
||||
Some(Right)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else if to.dir == self.dir {
|
||||
if to.pos == self.pos + self.dir.unit() {
|
||||
Some(Forward(1))
|
||||
} else if to.pos == self.pos - self.dir.unit() {
|
||||
Some(Backward(1))
|
||||
} else if to.pos == self.pos + Vec3::y() {
|
||||
Some(Up(1))
|
||||
} else if to.pos == self.pos - Vec3::y() {
|
||||
Some(Down(1))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Hash, PartialEq, Eq, Copy, Debug)]
|
||||
pub enum Direction {
|
||||
|
@ -80,8 +154,8 @@ pub fn nearest(from: Vec3, to: Vec3) -> Position {
|
|||
Direction::South
|
||||
}
|
||||
};
|
||||
(
|
||||
to - dir.unit(),
|
||||
Position {
|
||||
pos: to - dir.unit(),
|
||||
dir
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ use axum::{
|
|||
};
|
||||
use blocks::{World, Position};
|
||||
use mine::TurtleMineJob;
|
||||
use rstar::{self, AABB};
|
||||
use rstar::{self, AABB, RTree};
|
||||
|
||||
use const_format::formatcp;
|
||||
use hyper::body::Incoming;
|
||||
|
@ -23,7 +23,7 @@ use tokio::sync::{
|
|||
Mutex, RwLock, mpsc
|
||||
};
|
||||
use tower::Service;
|
||||
use turtle::{TurtleTask, Iota, Receiver, Sender, Turtle, TurtleUpdate, TurtleInfo};
|
||||
use turtle::{TurtleTask, Iota, Receiver, Sender, Turtle, TurtleUpdate, TurtleInfo, goto, TurtleCommand};
|
||||
|
||||
use crate::{blocks::Block, paths::route};
|
||||
|
||||
|
@ -37,16 +37,34 @@ mod turtle;
|
|||
#[derive(Serialize, Deserialize)]
|
||||
struct SavedState {
|
||||
turtles: Vec<turtle::Turtle>,
|
||||
tasks: Vec<VecDeque<TurtleMineJob>>,
|
||||
world: blocks::World,
|
||||
world: RTree<Block>,
|
||||
//chunkloaders: unimplemented!(),
|
||||
}
|
||||
|
||||
struct ControlState {
|
||||
saved: SavedState,
|
||||
struct LiveState {
|
||||
turtles: Vec<turtle::Turtle>,
|
||||
tasks: Vec<VecDeque<TurtleMineJob>>,
|
||||
world: blocks::World,
|
||||
}
|
||||
|
||||
type SharedControl = Arc<RwLock<ControlState>>;
|
||||
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 }
|
||||
}
|
||||
|
||||
fn from_save(save: SavedState) -> Self {
|
||||
Self { turtles: save.turtles, tasks: Vec::new(), world: World::from_tree(save.world) }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
type SharedControl = Arc<RwLock<LiveState>>;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Error> {
|
||||
|
@ -59,15 +77,13 @@ async fn main() -> Result<(), Error> {
|
|||
tokio::io::Result::Err(e) => match e.kind() {
|
||||
ErrorKind::NotFound => SavedState {
|
||||
turtles: Vec::new(),
|
||||
world: World::new(),
|
||||
tasks: Vec::new(),
|
||||
world: RTree::new(),
|
||||
},
|
||||
_ => panic!(),
|
||||
},
|
||||
};
|
||||
|
||||
let state = ControlState { saved: state,
|
||||
};
|
||||
let state = LiveState::from_save(state);
|
||||
|
||||
let state = SharedControl::new(RwLock::new(state));
|
||||
|
||||
|
@ -78,7 +94,7 @@ async fn main() -> Result<(), Error> {
|
|||
.route("/turtle/:id/setGoal", post(set_goal))
|
||||
.route("/turtle/:id/cancelTask", post(cancel))
|
||||
.route("/turtle/:id/info", get(turtle_info))
|
||||
.route("/turtle/:id/placeUp", get(place_up))
|
||||
//.route("/turtle/:id/placeUp", get(place_up))
|
||||
.route("/turtle/updateAll", get(update_turtles))
|
||||
.route("/flush", get(flush))
|
||||
.with_state(state.clone());
|
||||
|
@ -86,35 +102,34 @@ async fn main() -> Result<(), Error> {
|
|||
let server = safe_kill::serve(server).await;
|
||||
|
||||
println!("writing");
|
||||
write_to_disk(state).await?;
|
||||
write_to_disk(state.read().await.save().await).await?;
|
||||
|
||||
server.closed().await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
async fn write_to_disk(state: SharedControl) -> anyhow::Result<()> {
|
||||
let json = serde_json::to_string_pretty(&state.read().await.saved)?;
|
||||
tokio::fs::write("state.json", json).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn flush(State(state): State<SharedControl>) -> &'static str {
|
||||
write_to_disk(state).await.unwrap();
|
||||
write_to_disk(state.read().await.save().await).await.unwrap();
|
||||
|
||||
"ACK"
|
||||
}
|
||||
|
||||
async fn write_to_disk(state: SavedState) -> anyhow::Result<()> {
|
||||
let json = serde_json::to_string_pretty(&state)?;
|
||||
tokio::fs::write("state.json", json).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn create_turtle(
|
||||
State(state): State<SharedControl>,
|
||||
Json(req): Json<turtle::TurtleRegister>,
|
||||
) -> Json<turtle::TurtleResponse> {
|
||||
let state = &mut state.write().await;
|
||||
let id = state.saved.turtles.len() as u32;
|
||||
let id = state.turtles.len() as u32;
|
||||
let (send, receive) = mpsc::channel(1);
|
||||
state.saved.turtles.push(turtle::Turtle::with_channel(id, req.position, req.facing, req.fuel, send,receive));
|
||||
state.saved.tasks.push(VecDeque::new());
|
||||
state.turtles.push(turtle::Turtle::with_channel(id, Position::new(req.position, req.facing), req.fuel, send,receive));
|
||||
state.tasks.push(VecDeque::new());
|
||||
|
||||
|
||||
println!("new turtle: {id}");
|
||||
|
@ -130,10 +145,11 @@ async fn place_up(
|
|||
Path(id): Path<u32>,
|
||||
State(state): State<SharedControl>,
|
||||
) -> Json<TurtleInfo> {
|
||||
let turtle = state.write().await.saved.turtles.get(id as usize).unwrap()
|
||||
let turtle = state.read().await.turtles.get(id as usize).unwrap()
|
||||
.cmd();
|
||||
let response = turtle.execute(turtle::TurtleCommand::PlaceUp).await;
|
||||
|
||||
Json(turtle.execute(turtle::TurtleCommand::PlaceUp).await)
|
||||
Json(response)
|
||||
}
|
||||
|
||||
async fn set_goal(
|
||||
|
@ -141,10 +157,11 @@ async fn set_goal(
|
|||
State(state): State<SharedControl>,
|
||||
Json(req): Json<Position>,
|
||||
) -> &'static str {
|
||||
state.read().await.saved.turtles[id as usize].cmd().goto(req).await;
|
||||
//state.write().await.saved.tasks[id as usize].push_back(
|
||||
// TurtleMineJob::chunk(req.0)
|
||||
//);
|
||||
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()));
|
||||
|
||||
"ACK"
|
||||
}
|
||||
|
@ -153,7 +170,7 @@ async fn cancel(
|
|||
Path(id): Path<u32>,
|
||||
State(state): State<SharedControl>,
|
||||
) -> &'static str {
|
||||
state.write().await.saved.tasks[id as usize].pop_front();
|
||||
state.write().await.tasks[id as usize].pop_front();
|
||||
|
||||
"ACK"
|
||||
}
|
||||
|
@ -162,7 +179,7 @@ async fn update_turtles(State(state): State<SharedControl>) -> &'static str {
|
|||
state
|
||||
.write()
|
||||
.await
|
||||
.saved.turtles
|
||||
.turtles
|
||||
.iter_mut()
|
||||
.for_each(|t| t.pending_update = true);
|
||||
"ACK"
|
||||
|
@ -173,12 +190,11 @@ async fn turtle_info(
|
|||
State(state): State<SharedControl>,
|
||||
) -> Json<turtle::Turtle> {
|
||||
let state = &mut state.read().await;
|
||||
let turtle = &state.saved.turtles[id as usize];
|
||||
let turtle = &state.turtles[id as usize];
|
||||
|
||||
let cloned = Turtle::new(
|
||||
turtle.name.to_num(),
|
||||
turtle.position.to_owned().0,
|
||||
turtle.position.to_owned().1,
|
||||
turtle.position,
|
||||
turtle.fuel
|
||||
);
|
||||
|
||||
|
@ -194,7 +210,7 @@ async fn command(
|
|||
|
||||
println!("{:?}", &req);
|
||||
|
||||
if id as usize > state.saved.turtles.len() {
|
||||
if id as usize > state.turtles.len() {
|
||||
return Json(turtle::TurtleCommand::Update);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,58 +1,62 @@
|
|||
use std::rc::Rc;
|
||||
|
||||
use crate::{
|
||||
blocks::{Block, World, Position, Direction, Vec3},
|
||||
blocks::{Block, World, Position, Direction, Vec3, WorldReadLock},
|
||||
};
|
||||
use pathfinding::prelude::astar;
|
||||
|
||||
|
||||
pub fn route(from: Position, to: Position, world: &World) -> Option<Vec<Position>> {
|
||||
pub async fn route(from: Position, to: Position, world: &World) -> Option<Vec<Position>> {
|
||||
// lock once, we'll be doing a lot of lookups
|
||||
let world = world.clone().lock().await;
|
||||
|
||||
// attempt at not crashing by looking infinitely into the abyss
|
||||
if world
|
||||
.locate_at_point(&to.0.into())
|
||||
.locate_at_point(&to.pos.into())
|
||||
.is_some_and(|b| difficulty(&b.name).is_none())
|
||||
{
|
||||
return None;
|
||||
}
|
||||
let route = astar(
|
||||
&from,
|
||||
move |p| next(p, world),
|
||||
|p1| (p1.0 - &to.0).abs().sum() as u32,
|
||||
move |p| next(p, &world),
|
||||
|p1| (p1.pos - &to.pos).abs().sum() as u32,
|
||||
|p| p == &to,
|
||||
)
|
||||
.unwrap();
|
||||
Some(route.0)
|
||||
}
|
||||
|
||||
fn next(from: &Position, world: &World) -> Vec<(Position, u32)> {
|
||||
fn next(from: &Position, world: &WorldReadLock) -> Vec<(Position, u32)> {
|
||||
let mut vec: Vec<(Position, u32)> = Vec::new();
|
||||
vec.push(((from.0, from.1.left()), 1));
|
||||
vec.push(((from.0, from.1.right()), 1));
|
||||
|
||||
fn insert(
|
||||
vec: &mut Vec<(Position, u32)>,
|
||||
point: Vec3,
|
||||
orientation: Direction,
|
||||
world: &World,
|
||||
world: &WorldReadLock,
|
||||
unknown: Option<u32>,
|
||||
) {
|
||||
world
|
||||
.locate_at_point(&point.into())
|
||||
.map_or(unknown, |b| difficulty(&b.name))
|
||||
.map(|d| vec.push(((point, orientation), d)));
|
||||
.map(|d| vec.push((Position::new(point, orientation), d)));
|
||||
}
|
||||
|
||||
let ahead = from.0 + from.1.unit();
|
||||
insert(&mut vec, ahead, from.1, world, UNKNOWN);
|
||||
vec.push((Position::new(from.pos, from.dir.left()), 1));
|
||||
vec.push((Position::new(from.pos, from.dir.right()), 1));
|
||||
|
||||
//let behind = from.0 - from.1.unit();
|
||||
//insert(&mut vec, behind, from.1, world, None);
|
||||
let ahead = from.pos + from.dir.unit();
|
||||
insert(&mut vec, ahead, from.dir, world, UNKNOWN);
|
||||
|
||||
let above = from.0 + Vec3::y();
|
||||
insert(&mut vec, above, from.1, world, UNKNOWN);
|
||||
//let behind = from.pos - from.dir.unit();
|
||||
//insert(&mut vec, behind, from.dir, world, None);
|
||||
|
||||
let below = from.0 - Vec3::y();
|
||||
insert(&mut vec, below, from.1, world, UNKNOWN);
|
||||
let above = from.pos + Vec3::y();
|
||||
insert(&mut vec, above, from.dir, world, UNKNOWN);
|
||||
|
||||
let below = from.pos - Vec3::y();
|
||||
insert(&mut vec, below, from.dir, world, UNKNOWN);
|
||||
|
||||
vec
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ use crate::blocks::Block;
|
|||
use crate::blocks::Direction;
|
||||
use crate::blocks::Position;
|
||||
use crate::blocks::Vec3;
|
||||
use crate::blocks::World;
|
||||
use crate::blocks::nearest;
|
||||
use crate::mine::TurtleMineJob;
|
||||
|
||||
|
@ -13,8 +14,9 @@ use anyhow::Context;
|
|||
use tokio::sync::RwLock;
|
||||
use tokio::sync::mpsc;
|
||||
use tokio::sync::oneshot;
|
||||
use tokio::sync::oneshot::channel;
|
||||
|
||||
use super::ControlState;
|
||||
use super::LiveState;
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::future::Ready;
|
||||
|
@ -69,39 +71,52 @@ pub type Receiver = mpsc::Receiver<(TurtleCommand, oneshot::Sender<TurtleInfo>)>
|
|||
|
||||
impl Default for Turtle {
|
||||
fn default() -> Self {
|
||||
let (sender, receiver) = mpsc::channel(1);
|
||||
Self {
|
||||
name: Name::from_num(0),
|
||||
fuel: Default::default(),
|
||||
queued_movement: Default::default(),
|
||||
position: (Vec3::zeros(), Direction::North),
|
||||
position: Position::new(Vec3::zeros(), Direction::North),
|
||||
goal: None,
|
||||
pending_update: Default::default(),
|
||||
callback: None,
|
||||
sender: None,
|
||||
receiver: None,
|
||||
sender: Some(Arc::new(sender)),
|
||||
receiver: Some(receiver),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Turtle {
|
||||
pub(crate) fn new(id: u32, position: Vec3, facing: Direction, fuel: usize) -> Self {
|
||||
pub(crate) fn new(id: u32, position: Position, fuel: usize) -> Self {
|
||||
Self {
|
||||
name: Name::from_num(id),
|
||||
fuel,
|
||||
queued_movement: Vec3::new(0, 0, 0),
|
||||
position: (position, facing),
|
||||
position,
|
||||
pending_update: true,
|
||||
..Default::default()
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_channel(id: u32, position: Vec3, facing: Direction, fuel: usize, sender: Sender, receiver: Receiver) -> Self {
|
||||
/// Similar turtle for serialization
|
||||
pub fn info(&self) -> Self {
|
||||
Self {
|
||||
name: self.name,
|
||||
fuel: self.fuel,
|
||||
position: self.position,
|
||||
pending_update: self.pending_update,
|
||||
queued_movement: self.queued_movement,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_channel(id: u32, position: Position, fuel: usize, sender: Sender, receiver: Receiver) -> Self {
|
||||
Self {
|
||||
name: Name::from_num(id),
|
||||
fuel,
|
||||
queued_movement: Vec3::new(0, 0, 0),
|
||||
position: (position, facing),
|
||||
position,
|
||||
pending_update: true,
|
||||
sender: Some(Arc::new(sender)),
|
||||
receiver: Some(receiver),
|
||||
|
@ -116,6 +131,7 @@ impl Turtle {
|
|||
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TurtleCommander {
|
||||
sender: Arc<Sender>,
|
||||
}
|
||||
|
@ -130,28 +146,27 @@ impl TurtleCommander {
|
|||
}
|
||||
}
|
||||
|
||||
async fn goto(cmd: &TurtleCommander, recent: TurtleInfo, pos: Position, world: &rstar::RTree<Block>) -> Option<()> {
|
||||
pub async fn goto(cmd: TurtleCommander, recent: TurtleInfo, pos: Position, world: World) -> Option<()> {
|
||||
let mut recent = recent.pos;
|
||||
loop {
|
||||
if recent == pos {
|
||||
break;
|
||||
}
|
||||
|
||||
let route = route(recent, pos, world)?;
|
||||
let route = route(recent, pos, &world).await?;
|
||||
|
||||
let steps: Vec<TurtleCommand> = route.iter().map_windows(|[from,to]| difference(**from,**to).unwrap()).collect();
|
||||
let steps: Vec<TurtleCommand> = 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.locate_at_point(&next_position.0.into()).unwrap().name != "minecraft:air" {
|
||||
if world.occupied(next_position.pos).await {
|
||||
break 'route;
|
||||
}
|
||||
|
||||
let state = cmd.execute(command).await;
|
||||
recent = state.pos;
|
||||
|
||||
}
|
||||
}
|
||||
Some(())
|
||||
|
@ -159,18 +174,18 @@ async fn goto(cmd: &TurtleCommander, recent: TurtleInfo, pos: Position, world: &
|
|||
|
||||
pub(crate) async fn process_turtle_update(
|
||||
id: u32,
|
||||
state: &mut ControlState,
|
||||
state: &mut LiveState,
|
||||
update: TurtleUpdate,
|
||||
) -> anyhow::Result<TurtleCommand> {
|
||||
let turtle = state
|
||||
.saved.turtles
|
||||
.turtles
|
||||
.get_mut(id as usize)
|
||||
.context("nonexisting turtle")?;
|
||||
let tasks = state
|
||||
.saved.tasks
|
||||
.tasks
|
||||
.get_mut(id as usize)
|
||||
.context("state gone?????").unwrap();
|
||||
let world = &mut state.saved.world;
|
||||
let world = &mut state.world;
|
||||
|
||||
if turtle.pending_update {
|
||||
turtle.pending_update = false;
|
||||
|
@ -180,30 +195,27 @@ pub(crate) async fn process_turtle_update(
|
|||
if turtle.fuel != update.fuel {
|
||||
turtle.fuel = update.fuel;
|
||||
|
||||
turtle.position.0 += turtle.queued_movement;
|
||||
turtle.position.pos += turtle.queued_movement;
|
||||
turtle.queued_movement = Vec3::zeros();
|
||||
}
|
||||
|
||||
let above = Block {
|
||||
name: update.above.clone(),
|
||||
pos: turtle.position.0 + Vec3::y(),
|
||||
pos: turtle.position.pos + Vec3::y(),
|
||||
};
|
||||
world.remove_at_point(&above.pos.into());
|
||||
world.insert(above.clone());
|
||||
world.set(above.clone()).await;
|
||||
|
||||
let ahead = Block {
|
||||
name: update.ahead.clone(),
|
||||
pos: turtle.position.0 + turtle.position.1.clone().unit(),
|
||||
pos: turtle.position.pos + turtle.position.dir.unit(),
|
||||
};
|
||||
world.remove_at_point(&ahead.pos.into());
|
||||
world.insert(ahead.clone());
|
||||
world.set(ahead.clone()).await;
|
||||
|
||||
let below = Block {
|
||||
name: update.below.clone(),
|
||||
pos: turtle.position.0 - Vec3::y(),
|
||||
pos: turtle.position.pos - Vec3::y(),
|
||||
};
|
||||
world.remove_at_point(&below.pos.into());
|
||||
world.insert(below.clone());
|
||||
world.set(below.clone()).await;
|
||||
|
||||
let info = TurtleInfo::from_update(update, turtle.name.clone(), turtle.position.clone());
|
||||
|
||||
|
@ -215,65 +227,18 @@ pub(crate) async fn process_turtle_update(
|
|||
if let Some((cmd, ret)) = recv.try_recv().ok() {
|
||||
turtle.callback = Some(ret);
|
||||
|
||||
match cmd {
|
||||
TurtleCommand::Left => turtle.position.dir = turtle.position.dir.left(),
|
||||
TurtleCommand::Right => turtle.position.dir = turtle.position.dir.right(),
|
||||
_ => {}
|
||||
}
|
||||
return Ok(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(goal) = turtle.goal.take().or_else(|| tasks.front_mut().map(|t| t.next(&turtle))) {
|
||||
let command = match goal {
|
||||
Iota::End => {
|
||||
tasks.pop_front();
|
||||
TurtleCommand::Wait(0) // TODO: fix
|
||||
},
|
||||
Iota::Goto(pos) => {
|
||||
println!("gogto: {:?}", pos);
|
||||
pathstep(turtle, world, pos).unwrap()
|
||||
},
|
||||
Iota::Mine(pos) => {
|
||||
let pos = nearest(turtle.position.0, pos);
|
||||
|
||||
if pos == turtle.position {
|
||||
TurtleCommand::Dig
|
||||
} else {
|
||||
pathstep(turtle, world, pos).unwrap()
|
||||
}
|
||||
},
|
||||
Iota::Execute(cmd) => {
|
||||
cmd
|
||||
},
|
||||
};
|
||||
|
||||
println!("Order: {:?}", command);
|
||||
return Ok(command);
|
||||
};
|
||||
|
||||
Ok(TurtleCommand::Wait(3))
|
||||
}
|
||||
|
||||
fn pathstep(turtle: &mut Turtle, world: &mut rstar::RTree<Block>, goal: Position) -> Option<TurtleCommand> {
|
||||
// TODO: memoize this whenever we aren't digging
|
||||
let route = route(turtle.position, goal, world)?;
|
||||
let mut next_move = difference(route[0], route[1])?;
|
||||
if world
|
||||
.locate_at_point(&route[1].0.into())
|
||||
.is_some_and(|b| b.name != "minecraft:air")
|
||||
{
|
||||
next_move = match next_move {
|
||||
TurtleCommand::Up(_) => TurtleCommand::DigUp,
|
||||
TurtleCommand::Down(_) => TurtleCommand::DigDown,
|
||||
TurtleCommand::Forward(_) => TurtleCommand::Dig,
|
||||
_ => next_move,
|
||||
}
|
||||
}
|
||||
turtle.queued_movement = next_move.delta(turtle.position.1);
|
||||
match next_move {
|
||||
TurtleCommand::Left => turtle.position.1 = turtle.position.1.left(),
|
||||
TurtleCommand::Right => turtle.position.1 = turtle.position.1.right(),
|
||||
_ => {}
|
||||
}
|
||||
return Some(next_move);
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub(crate) enum TurtleCommand {
|
||||
Wait(u32),
|
||||
|
@ -354,35 +319,6 @@ pub(crate) struct TurtleResponse {
|
|||
pub(crate) command: TurtleCommand,
|
||||
}
|
||||
|
||||
/// Get a turtle command to map two adjacent positions
|
||||
fn difference(from: Position, to: Position) -> Option<TurtleCommand> {
|
||||
use TurtleCommand::*;
|
||||
|
||||
if from.0 == to.0 {
|
||||
if to.1 == from.1.left() {
|
||||
Some(Left)
|
||||
} else if to.1 == from.1.right() {
|
||||
Some(Right)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else if to.1 == from.1 {
|
||||
if to.0 == from.0 + from.1.unit() {
|
||||
Some(Forward(1))
|
||||
} else if to.0 == from.0 - from.1.unit() {
|
||||
Some(Backward(1))
|
||||
} else if to.0 == from.0 + Vec3::y() {
|
||||
Some(Up(1))
|
||||
} else if to.0 == from.0 - Vec3::y() {
|
||||
Some(Down(1))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub enum Iota {
|
||||
End,
|
||||
|
|
Loading…
Reference in a new issue