Compare commits

..

4 Commits

7 changed files with 302 additions and 18 deletions

View File

@@ -1,5 +1,6 @@
package bin; package bin;
import kernel.turtle.Types.ToolSide;
import lib.turtle.Helper; import lib.turtle.Helper;
import kernel.turtle.TurtleMutex; import kernel.turtle.TurtleMutex;
import kernel.turtle.Turtle; import kernel.turtle.Turtle;
@@ -116,6 +117,28 @@ class TurtleCtl extends CLIAppBase {
var len = args.getInt("length"); var len = args.getInt("length");
return asynPerform(Helper.combine.bind([Turtle.digEmpty.bind(Front), Turtle.forward, Turtle.digEmpty.bind(Up)]), len); return asynPerform(Helper.combine.bind([Turtle.digEmpty.bind(Front), Turtle.forward, Turtle.digEmpty.bind(Up)]), len);
}, [Int("length")]); }, [Int("length")]);
registerSyncSubcommand("inspect", (args) -> {
var res = Turtle.inspect(Front);
switch res {
case Failure(err):
handle.writeLine("Failed: " + err);
return false;
case Success(data):
handle.writeLine('Name: ${data.name}');
handle.writeLine("Tags:");
for (tag in data.tags.sortByName()) {
handle.writeLine(' ${tag}');
}
handle.writeLine("State:");
handle.writeLine(data.state);
}
return true;
}, []);
} }
private function asynPerform(op:Void->Outcome<Noise, String>, times:Int):Future<Bool> { private function asynPerform(op:Void->Outcome<Noise, String>, times:Int):Future<Bool> {

View File

@@ -0,0 +1,76 @@
package kernel.peripherals;
import lib.BlockPos;
import lib.Tags;
import cc.Peripheral;
using tink.CoreApi;
using lua.Table;
@:structInit typedef ScanBlock = {
name:String,
tags:Tags,
pos:BlockPos
}
class GeoScanner implements IPeripheral {
public static inline final TYPE_NAME:String = "geoScanner";
private final addr:String;
public function new(addr:String) {
this.addr = addr;
}
public function getAddr():String {
return this.addr;
}
public function getType():String {
return TYPE_NAME;
}
/**
Returns the current time remaining until then next scan() can be ran.
**/
public function getScanCooldown():Int {
return Peripheral.call(this.addr, "getScanCooldown");
}
/**
Returns the cost in FE for a scan with the given radius.
Returns null of scan of this radius is not posible.
**/
public function cost(radius:Int):Null<Int> {
return Peripheral.call(this.addr, "cost", radius);
}
/**
Returns a list of data about all blocks in the radius.
X,Y,Z are relative to the geoscanner block.
Air blocks are not included.
List is unsorted.
**/
public function scan(radius:Int):Outcome<Array<ScanBlock>, String> {
// TODO: Handel fail state
var result:lua.Table<Int, {
x:Int,
y:Int,
z:Int,
name:String,
tags:lua.Table<Int, String>
}> = Peripheral.call(this.addr, "scan", radius);
return Success(result.toArray().map((e) -> {
name: e.name,
tags: Tags.fromIntTable(e.tags),
pos: new BlockPos(e.x, e.y, e.z)
}));
}
public function chunkAnalyze():Outcome<Map<String, Int>, String> {
var result:lua.Table<String, Int> = Peripheral.call(this.addr, "chunkAnalyze");
return Success(result.toMap());
}
}

View File

@@ -1,5 +1,9 @@
package kernel.peripherals; package kernel.peripherals;
import kernel.log.Log;
import lib.Debug;
import cc.Peripheral;
import lib.Tags;
import lib.Item; import lib.Item;
import cc.periphs.ItemStorage.ReducedItemInfo; import cc.periphs.ItemStorage.ReducedItemInfo;
import lua.Table; import lua.Table;
@@ -22,7 +26,7 @@ class ItemInfo {
class DetailedItemInfo extends ItemInfo { class DetailedItemInfo extends ItemInfo {
public final displayName:String; public final displayName:String;
public final maxCount:Int; public final maxCount:Int;
public final tags:Array<String>; public final tags:Tags;
public final durability:Null<Float>; public final durability:Null<Float>;
public final damage:Null<Int>; public final damage:Null<Int>;
public final maxDamage:Null<Int>; public final maxDamage:Null<Int>;
@@ -32,10 +36,7 @@ class DetailedItemInfo extends ItemInfo {
private function new(from:cc.periphs.ItemStorage.DetailedItemInfo) { private function new(from:cc.periphs.ItemStorage.DetailedItemInfo) {
super(from); super(from);
this.tags = []; this.tags = Tags.fromBoolTable(from.tags);
for (k => _ in Table.toMap(from.tags)) {
this.tags.push(k);
}
this.displayName = from.displayName; this.displayName = from.displayName;
this.maxCount = from.maxCount; this.maxCount = from.maxCount;
@@ -55,11 +56,9 @@ class Inventory implements IPeripheral {
public static inline final TYPE_NAME:String = "inventory"; public static inline final TYPE_NAME:String = "inventory";
private final addr:String; private final addr:String;
private final native:cc.periphs.ItemStorage;
public function new(addr:String) { public function new(addr:String) {
this.addr = addr; this.addr = addr;
this.native = cc.Peripheral.wrap(addr);
} }
public function getAddr():String { public function getAddr():String {
@@ -74,18 +73,18 @@ class Inventory implements IPeripheral {
Get the size of this inventory. Get the size of this inventory.
**/ **/
public function size():Int { public function size():Int {
return this.native.size(); return Peripheral.call(this.addr, "size");
} }
/** /**
List all items in this inventory. List all items in this inventory.
**/ **/
public function list():Map<Int, ItemInfo> { public function list():Map<Int, ItemInfo> {
var list = Table.toArray(this.native.list()); var list:Map<Int, ReducedItemInfo> = Table.toMap(Peripheral.call(this.addr, "list"));
var rtn:Map<Int, ItemInfo> = new Map(); var rtn:Map<Int, ItemInfo> = new Map();
for (k => v in list) { for (k => v in list) {
rtn.set(k, new ItemInfo(v)); rtn.set(k - 1, new ItemInfo(v));
} }
return rtn; return rtn;
@@ -95,7 +94,7 @@ class Inventory implements IPeripheral {
Get detailed information about an item. Get detailed information about an item.
**/ **/
public function getItemDetail(slot:Int):DetailedItemInfo { public function getItemDetail(slot:Int):DetailedItemInfo {
return new DetailedItemInfo(this.native.getItemDetail(slot)); return new DetailedItemInfo(Peripheral.call(this.addr, "getItemDetail", slot + 1));
} }
/** /**
@@ -105,20 +104,20 @@ class Inventory implements IPeripheral {
Keep in mind that this does not depend on that item stored in this slot. Keep in mind that this does not depend on that item stored in this slot.
**/ **/
public function getItemLimit(slot:Int):Int { public function getItemLimit(slot:Int):Int {
return this.native.getItemLimit(slot); return Peripheral.call(this.addr, "getItemLimit", slot + 1);
} }
/** /**
Push items from one inventory to another connected one. Push items from one inventory to another connected one. Needs to be on the same wired network.
**/ **/
public function pushItems(toSlot:Int, to:String, ?limit:Int, ?toSlot:Int):Int { public function pushItems(to:String, fromSlot:Int, ?limit:Int, ?toSlot:Int):Int {
return this.native.pushItems(to, toSlot, limit, toSlot); return Peripheral.call(this.addr, "pushItems", to, fromSlot + 1, limit, toSlot);
} }
/** /**
Pull items from a connected inventory into this one. Pull items from a connected inventory into this one. Needs to be on the same wired network.
**/ **/
public function pullItems(from:String, fromSlot:Int, ?limit:Int, ?toSlot:Int):Int { public function pullItems(from:String, fromSlot:Int, ?limit:Int, ?toSlot:Int):Int {
return this.native.pullItems(from, fromSlot, limit, toSlot); return Peripheral.call(this.addr, "pullItems", from, fromSlot + 1, limit, toSlot);
} }
} }

View File

@@ -1,6 +1,5 @@
package kernel.peripherals; package kernel.peripherals;
import kernel.log.Log;
import kernel.peripherals.Modem; import kernel.peripherals.Modem;
import kernel.peripherals.Screen; import kernel.peripherals.Screen;
@@ -83,6 +82,8 @@ class Peripheral {
return getRedstone(addr); return getRedstone(addr);
case Inventory.TYPE_NAME: case Inventory.TYPE_NAME:
return getInventory(addr); return getInventory(addr);
case GeoScanner.TYPE_NAME:
return getGeoScanner(addr);
} }
return null; return null;
@@ -180,4 +181,15 @@ class Peripheral {
public static function getAllInventorys():Array<Inventory> { public static function getAllInventorys():Array<Inventory> {
return [for (addr in findAddrByType(Inventory.TYPE_NAME)) new Inventory(addr)]; return [for (addr in findAddrByType(Inventory.TYPE_NAME)) new Inventory(addr)];
} }
public static function getGeoScanner(addr:String):Null<GeoScanner> {
var addr = safeGetAddr(addr, GeoScanner.TYPE_NAME);
if (addr == null)
return null;
return new GeoScanner(addr);
}
public static function getAllGeoScanners():Array<GeoScanner> {
return [for (addr in findAddrByType(GeoScanner.TYPE_NAME)) new GeoScanner(addr)];
}
} }

12
src/lib/Block.hx Normal file
View File

@@ -0,0 +1,12 @@
package lib;
@:structInit
class Block {
public final name:PaN;
public final tags:Tags;
public function new(name:String, tags:Tags) {
this.name = name;
this.tags = tags;
}
}

114
src/lib/BlockBlockMap.hx Normal file
View File

@@ -0,0 +1,114 @@
package lib;
import kernel.peripherals.GeoScanner.ScanBlock;
import lib.BlockMap;
@:forward
abstract BlockBlockMap(BlockMap<Block>) {
public inline function new() {
this = new BlockMap<Block>();
}
public static function fromScan(scan:Array<ScanBlock>):BlockBlockMap {
var map:BlockBlockMap = new BlockBlockMap();
for (block in scan) {
map.set(block.pos, new Block(block.name, block.tags));
}
return map;
}
/**
Navigate from one point into another.
Returns the path to take.
Based on A*
**/
public function nav(from:BlockPos, to:BlockPos):Array<BlockPos> {
if (!canPass(to)) {
trace("End is blocked");
return [];
}
var openSet = new PriorityQueue();
openSet.insert(from, 0);
var cameFrom:BlockMap<BlockPos> = new BlockMap();
var gCost:BlockMap<Int> = new BlockMap(); // G cost is the distance from start
gCost.set(from, 0);
var fCost:BlockMap<Float> = new BlockMap(); // F cost is the combines cost of moving to this pos (G cost) + the distnace to the end
fCost.set(from, from.distance(to));
while (!openSet.isEmpty()) {
var current = openSet.extractMin(); // Check the pos with the lowest F cost
if (current.equals(to)) {
var totalPath = [];
var currentPathPos = to;
while (cameFrom.exists(currentPathPos)) {
totalPath.unshift(currentPathPos);
currentPathPos = cameFrom.get(currentPathPos);
}
totalPath.unshift(from);
return totalPath;
}
for (neighbor in current.neighbors()) {
if (!canPass(neighbor)) {
continue;
}
var newGCost = gCost.get(current) + 1; // Movment cost to neighbor is always 1 as we can't move diagonally
if (newGCost < (gCost.get(neighbor) ?? 9999)) {
cameFrom.set(neighbor, current);
gCost.set(neighbor, newGCost);
fCost.set(neighbor, newGCost + neighbor.distance(to));
if (!openSet.containsElement(neighbor)) {
openSet.insert(neighbor, fCost.get(neighbor));
}
}
}
}
return [];
}
/**
Some block can be passed by the turtle.
Some blocks get destroyed by it.
**/
public function canPass(k:BlockPos):Bool {
var block = this.get(k);
if (block == null) {
return true;
}
switch block.name {
case "minecraft:water":
return true;
case "minecaft:lava":
case "mimecraft:grass":
case "minecraft:hanging_roots":
case "minecraft:warped_roots":
case "minecraft:dead_bush":
case "minecraft:fern":
case "minecraft:vine":
case "minecraft:glow_lichen":
case "minecraft:seagrass":
case "minecraft:crimson_roots":
case "minecraft:nether_sprouts":
case "minecraft:snow":
case "minecraft:rose_bush":
case "minecraft:fire":
return true;
}
return false;
}
}

48
src/lib/BlockMap.hx Normal file
View File

@@ -0,0 +1,48 @@
package lib;
import haxe.ds.IntMap;
/**
Map values to positions in 3D space. For representing blocks from a scan see `BlockBlockMap`.
The only reason this exsists is that the abstract class for BlockPos does not
satisfy the constraint of HashMap. The hashCode function needs to be on a real class and not on
an abstract class. Thanks Obama.
**/
@:forward(iterator, clear)
abstract BlockMap<T>(IntMap<T>) {
/**
Creates a new HashMap.
**/
public inline function new() {
this = new IntMap();
}
/**
See `Map.set`
**/
@:arrayAccess public inline function set(k:BlockPos, v:T) {
this.set(k.hashCode(), v);
}
/**
See `Map.get`
**/
@:arrayAccess public inline function get(k:BlockPos) {
return this.get(k.hashCode());
}
/**
See `Map.exists`
**/
public inline function exists(k:BlockPos) {
return this.exists(k.hashCode());
}
/**
See `Map.remove`
**/
public inline function remove(k:BlockPos) {
return this.remove(k.hashCode());
}
}