go back for diamonds
This commit is contained in:
parent
d9a80f2a2b
commit
7d9cd93d1c
2 changed files with 258 additions and 1 deletions
|
@ -1,4 +1,4 @@
|
||||||
#![feature(iter_map_windows)]
|
#![feature(iter_map_windows, iter_collect_into)]
|
||||||
|
|
||||||
use std::{collections::VecDeque, io::ErrorKind, sync::Arc, env::args, path};
|
use std::{collections::VecDeque, io::ErrorKind, sync::Arc, env::args, path};
|
||||||
|
|
||||||
|
|
257
server/src/mine.rs
Normal file
257
server/src/mine.rs
Normal file
|
@ -0,0 +1,257 @@
|
||||||
|
use hilbert_index::FromHilbertIndex;
|
||||||
|
use nalgebra::SimdValue;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use rstar::{self, AABB};
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
|
use crate::{blocks::{Position, Vec3, Block, Direction}, turtle::{TurtleTask, Iota, self, Turtle, TurtleCommand, TurtleCommander, TurtleCommandResponse, InventorySlot}};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct TurtleMineJobParams {
|
||||||
|
pub chunks: Vec<Vec3>,
|
||||||
|
pub method: TurtleMineMethod,
|
||||||
|
pub refuel: Position,
|
||||||
|
pub storage: Position,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct TurtleMineJob {
|
||||||
|
to_mine: VecDeque<Vec3>,
|
||||||
|
mined: u32,
|
||||||
|
mined_chunks: u32,
|
||||||
|
params: TurtleMineJobParams,
|
||||||
|
state: State,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
enum State {
|
||||||
|
Mining,
|
||||||
|
Refueling,
|
||||||
|
Storing,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub enum TurtleMineMethod {
|
||||||
|
Clear,
|
||||||
|
Hilbert,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_hilbert_chunk(n: i32, min: Vec3, half: bool) -> Option<Vec3> {
|
||||||
|
let max = min + Vec3::new(16, 16, 16);
|
||||||
|
let level = if half {
|
||||||
|
3
|
||||||
|
} else { 4 };
|
||||||
|
|
||||||
|
let point: [usize; 3] = FromHilbertIndex::from_hilbert_index(&(n as usize), level);
|
||||||
|
|
||||||
|
let mut point = Vec3::new(point[0] as i32, point[1] as i32, point[2] as i32);
|
||||||
|
|
||||||
|
dbg!(point);
|
||||||
|
|
||||||
|
point = if half {
|
||||||
|
point * 2
|
||||||
|
} else {
|
||||||
|
point
|
||||||
|
};
|
||||||
|
|
||||||
|
point += min;
|
||||||
|
if point > max {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(dbg!(point))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TurtleMineJob {
|
||||||
|
pub fn chunk(point: Vec3) -> Self {
|
||||||
|
//let chunk_min = Vec3::new(
|
||||||
|
// point.x - point.x % 16,
|
||||||
|
// point.y - point.y % 16,
|
||||||
|
// point.z - point.z % 16);
|
||||||
|
let chunk_min = point;
|
||||||
|
TurtleMineJob { to_mine: VecDeque::new(), mined: 0, mined_chunks: 0,
|
||||||
|
params: TurtleMineJobParams { chunks: vec![chunk_min], method: TurtleMineMethod::Hilbert, refuel:
|
||||||
|
Position::new(Vec3::new(-30,65,-44), Direction::South),
|
||||||
|
storage: Position::new(Vec3::new(-29,65,-44), Direction::South)
|
||||||
|
},
|
||||||
|
state: State::Mining }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TurtleTask for TurtleMineJob {
|
||||||
|
fn handle_block(&mut self, block: Block) {
|
||||||
|
// TODO: more logic
|
||||||
|
if block.name.contains("ore") {
|
||||||
|
self.to_mine.push_back(block.pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next(&mut self, turtle: &Turtle) -> Iota {
|
||||||
|
if (turtle.fuel as i32) < (turtle.position.pos - self.params.refuel.pos).abs().sum() * 2 {
|
||||||
|
self.state = State::Refueling;
|
||||||
|
}
|
||||||
|
println!("{:?}", self.state);
|
||||||
|
println!("{}m to depot",(turtle.position.pos - self.params.refuel.pos).abs().sum());
|
||||||
|
|
||||||
|
match self.state {
|
||||||
|
State::Mining => {
|
||||||
|
if let Some(block) = self.to_mine.pop_back() {
|
||||||
|
return Iota::Mine(block.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(block) = next_hilbert_chunk(self.mined as i32, self.params.chunks[self.mined_chunks as usize], false) {
|
||||||
|
self.mined += 1;
|
||||||
|
return Iota::Mine(block.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Iota::End
|
||||||
|
},
|
||||||
|
State::Refueling => {
|
||||||
|
if turtle.position != self.params.refuel {
|
||||||
|
Iota::Goto(self.params.refuel)
|
||||||
|
} else {
|
||||||
|
self.state = State::Mining;
|
||||||
|
Iota::Execute(TurtleCommand::Refuel)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State::Storing => {
|
||||||
|
if turtle.position != self.params.storage {
|
||||||
|
Iota::Goto(self.params.storage)
|
||||||
|
} else {
|
||||||
|
self.state = State::Mining;
|
||||||
|
Iota::Execute(TurtleCommand::Poweroff)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
use TurtleCommand::*;
|
||||||
|
|
||||||
|
/// Things to leave in the field (not worth fuel)
|
||||||
|
const USELESS: [&str; 4] = [
|
||||||
|
"minecraft:dirt",
|
||||||
|
"minecraft:cobblestone",
|
||||||
|
"minecraft:cobbled_deepslate",
|
||||||
|
"minecraft:diorite",
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Things that are desirable
|
||||||
|
const VALUABLE: [&str; 1] = [
|
||||||
|
"ore",
|
||||||
|
];
|
||||||
|
|
||||||
|
pub async fn mine(turtle: TurtleCommander, pos: Vec3, fuel: Position, storage: Position) -> Option<()> {
|
||||||
|
let chunk = Vec3::new(4,4,4);
|
||||||
|
let volume = chunk.x * chunk.y * chunk.z;
|
||||||
|
let mut pos = pos;
|
||||||
|
let mut valuables = Vec::new();
|
||||||
|
loop {
|
||||||
|
mine_chunk(turtle.clone(), pos, chunk).await?;
|
||||||
|
|
||||||
|
turtle.world().lock().await
|
||||||
|
.locate_within_distance(pos.into(), chunk.map(|n| n.pow(2)).sum())
|
||||||
|
.filter(|n| n.name != "minecraft:air")
|
||||||
|
.filter(|n| VALUABLE.iter().any(|v| n.name.contains(v)))
|
||||||
|
.map(|b|b.pos).collect_into(&mut valuables);
|
||||||
|
|
||||||
|
dump_filter(turtle.clone(), |i| USELESS.iter().any(|u| **u == i.name)).await;
|
||||||
|
|
||||||
|
while let Some(block) = valuables.pop() {
|
||||||
|
let near = turtle.goto_adjacent(block).await?;
|
||||||
|
turtle.execute(dbg!(near.dig(block))?).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (turtle.fuel().await as f64) < (volume + (fuel.pos-turtle.pos().await.pos).abs().sum()) as f64 * 1.1 {
|
||||||
|
println!("refueling");
|
||||||
|
turtle.goto(fuel).await?;
|
||||||
|
refuel(turtle.clone()).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let turtle::TurtleCommandResponse::Item(_) = turtle.execute(ItemInfo(13)).await.ret {
|
||||||
|
println!("storage rtb");
|
||||||
|
turtle.goto(storage).await?;
|
||||||
|
dump(turtle.clone()).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pos += Vec3::z() * chunk.z;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn mine_chunk(turtle: TurtleCommander, pos: Vec3, chunk: Vec3) -> Option<()> {
|
||||||
|
let turtle = turtle.clone();
|
||||||
|
let volume = chunk.x * chunk.y * chunk.z;
|
||||||
|
|
||||||
|
for n in (0..volume).map(|n| fill(chunk, n) + pos) {
|
||||||
|
if turtle.world().get(n).await.is_some_and(|b| b.name == "minecraft:air") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let near = turtle.goto_adjacent(n).await?;
|
||||||
|
|
||||||
|
turtle.execute(dbg!(near.dig(n))?).await;
|
||||||
|
|
||||||
|
}
|
||||||
|
Some(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn refuel(turtle: TurtleCommander) {
|
||||||
|
turtle.execute(Select(16)).await;
|
||||||
|
turtle.execute(DropUp(64)).await;
|
||||||
|
let limit = turtle.fuel_limit().await;
|
||||||
|
while turtle.fuel().await < limit {
|
||||||
|
turtle.execute(SuckFront(64)).await;
|
||||||
|
let re = turtle.execute(Refuel).await;
|
||||||
|
if let TurtleCommandResponse::Failure = re.ret {
|
||||||
|
// partial refuel, good enough
|
||||||
|
println!("only received {} fuel", turtle.fuel().await);
|
||||||
|
if turtle.fuel().await > 1000 {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
turtle.execute(Wait(15)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
turtle.execute(DropFront(64)).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn dump(turtle: TurtleCommander) {
|
||||||
|
for i in 1..=16 {
|
||||||
|
turtle.execute(Select(i)).await;
|
||||||
|
turtle.execute(DropFront(64)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Dump all items that match the predicate
|
||||||
|
async fn dump_filter<F>(turtle: TurtleCommander, mut filter: F)
|
||||||
|
where F: FnMut(InventorySlot) -> bool {
|
||||||
|
for i in 1..=16 {
|
||||||
|
if let TurtleCommandResponse::Item(item) = turtle.execute(Select(i)).await.ret {
|
||||||
|
if filter(item) {
|
||||||
|
turtle.execute(DropFront(64)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// zig from 0 to x and back, stopping on each end
|
||||||
|
fn step(n: i32, x: i32) -> i32 {
|
||||||
|
let half = n%x;
|
||||||
|
let full = n%(2*x);
|
||||||
|
if full > x - 1 {
|
||||||
|
x - half - 1
|
||||||
|
} else {
|
||||||
|
full
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// generates a sequence of adjacent positions within a volume
|
||||||
|
fn fill(scale: Vec3, n: i32) -> Vec3 {
|
||||||
|
assert!(n < scale.x * scale.y * scale.z);
|
||||||
|
Vec3::new(
|
||||||
|
step(n,scale.x),
|
||||||
|
step(n/scale.x, scale.y),
|
||||||
|
step(n/scale.x/scale.y, scale.z)
|
||||||
|
)
|
||||||
|
}
|
Loading…
Reference in a new issue