Compare commits
2 Commits
e04021425a
...
5c71ab1c30
| Author | SHA1 | Date | |
|---|---|---|---|
|
5c71ab1c30
|
|||
|
36f97f09d5
|
@@ -1,62 +0,0 @@
|
|||||||
package bin;
|
|
||||||
|
|
||||||
import lib.turtle.InvManager;
|
|
||||||
import lib.CLIAppBase;
|
|
||||||
|
|
||||||
using tink.CoreApi;
|
|
||||||
|
|
||||||
@:build(macros.Binstore.includeBin("Turtle", ["turtle", "t"]))
|
|
||||||
class Turtle extends CLIAppBase {
|
|
||||||
public function new() {
|
|
||||||
registerSyncSubcommand("forward", (args) -> {
|
|
||||||
return checkAvailable() && perform(kernel.turtle.Turtle.forward());
|
|
||||||
});
|
|
||||||
|
|
||||||
registerSyncSubcommand("back", (args) -> {
|
|
||||||
return checkAvailable() && perform(kernel.turtle.Turtle.back());
|
|
||||||
});
|
|
||||||
|
|
||||||
registerSyncSubcommand("left", (args) -> {
|
|
||||||
return checkAvailable() && perform(kernel.turtle.Turtle.turnLeft());
|
|
||||||
});
|
|
||||||
|
|
||||||
registerSyncSubcommand("right", (args) -> {
|
|
||||||
return checkAvailable() && perform(kernel.turtle.Turtle.turnRight());
|
|
||||||
});
|
|
||||||
|
|
||||||
registerSyncSubcommand("up", (args) -> {
|
|
||||||
return checkAvailable() && perform(kernel.turtle.Turtle.up());
|
|
||||||
});
|
|
||||||
|
|
||||||
registerSyncSubcommand("down", (args) -> {
|
|
||||||
return checkAvailable() && perform(kernel.turtle.Turtle.down());
|
|
||||||
});
|
|
||||||
|
|
||||||
registerSyncSubcommand("defrag", (args) -> {
|
|
||||||
if (!checkAvailable()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
InvManager.defrag();
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private function checkAvailable():Bool {
|
|
||||||
if (!kernel.turtle.Turtle.isTurtle()) {
|
|
||||||
handle.write("This is not a turtle!");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function perform(outcome:Outcome<Noise, String>):Bool {
|
|
||||||
switch outcome {
|
|
||||||
case Success(_):
|
|
||||||
return true;
|
|
||||||
case Failure(error):
|
|
||||||
handle.write(error);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
103
src/bin/TurtleCtl.hx
Normal file
103
src/bin/TurtleCtl.hx
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
package bin;
|
||||||
|
|
||||||
|
import kernel.turtle.TurtleMutex;
|
||||||
|
import kernel.turtle.Turtle;
|
||||||
|
import lib.turtle.InvManager;
|
||||||
|
import lib.CLIAppBase;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
|
||||||
|
@:build(macros.Binstore.includeBin("Turtle", ["turtle", "t"]))
|
||||||
|
class TurtleCtl extends CLIAppBase {
|
||||||
|
public function new() {
|
||||||
|
registerAsyncSubcommand("f", (args) -> {
|
||||||
|
return asynPerform(Turtle.forward, parseTimes(args));
|
||||||
|
}, "<times>");
|
||||||
|
|
||||||
|
registerAsyncSubcommand("b", (args) -> {
|
||||||
|
return asynPerform(Turtle.back, parseTimes(args));
|
||||||
|
}, "<times>");
|
||||||
|
|
||||||
|
registerAsyncSubcommand("l", (args) -> {
|
||||||
|
return asynPerform(Turtle.turnLeft, parseTimes(args));
|
||||||
|
}, "<times>");
|
||||||
|
|
||||||
|
registerAsyncSubcommand("r", (args) -> {
|
||||||
|
return asynPerform(Turtle.turnRight, parseTimes(args));
|
||||||
|
}, "<times>");
|
||||||
|
|
||||||
|
registerAsyncSubcommand("u", (args) -> {
|
||||||
|
return asynPerform(Turtle.up, parseTimes(args));
|
||||||
|
}, "<times>");
|
||||||
|
|
||||||
|
registerAsyncSubcommand("d", (args) -> {
|
||||||
|
return asynPerform(Turtle.down, parseTimes(args));
|
||||||
|
}, "<times>");
|
||||||
|
|
||||||
|
registerAsyncSubcommand("defrag", (args) -> {
|
||||||
|
return asynPerform(() -> {
|
||||||
|
// TODO: when defrag can fail return that error
|
||||||
|
InvManager.defrag();
|
||||||
|
return Success(null);
|
||||||
|
}, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
registerSyncSubcommand("fuel", (args) -> {
|
||||||
|
var lvl = Turtle.getFuelLevel();
|
||||||
|
var limit = Turtle.getFuelLimit();
|
||||||
|
handle.writeLine('${lvl}/${limit} (${(lvl / limit) * 100}%)');
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private function asynPerform(op:Void->Outcome<Noise, String>, times:Int):Future<Bool> {
|
||||||
|
return new Future<Bool>((wakeup) -> {
|
||||||
|
if (times == 0) {
|
||||||
|
wakeup(false);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Turtle.isTurtle()) {
|
||||||
|
handle.write("This is not a turtle!");
|
||||||
|
wakeup(false);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!handle.claimTurtleMutex()) {
|
||||||
|
handle.writeLine("Failed to claim mutex");
|
||||||
|
wakeup(false);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
TurtleMutex.runInTThread(() -> {
|
||||||
|
for (i in 0...times) {
|
||||||
|
switch op() {
|
||||||
|
case Success(_):
|
||||||
|
case Failure(failure):
|
||||||
|
handle.writeLine('Failed: $failure');
|
||||||
|
wakeup(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wakeup(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private function parseTimes(args:Array<String>):Int {
|
||||||
|
if (args.length > 0) {
|
||||||
|
var num = Std.parseInt(args[0]);
|
||||||
|
if (num == null) {
|
||||||
|
handle.writeLine("Failed to parse args");
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
package lib.turtle;
|
|
||||||
|
|
||||||
import lib.Pos;
|
|
||||||
import lib.Pos3;
|
|
||||||
import kernel.turtle.Turtle;
|
|
||||||
|
|
||||||
using tink.CoreApi;
|
|
||||||
|
|
||||||
class TurtleExecuter {
|
|
||||||
private var instructions:Array<TurtleInstruction>;
|
|
||||||
|
|
||||||
public function new(instructions:Array<TurtleInstruction>) {
|
|
||||||
this.instructions = instructions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getRequiredFuel():Int {
|
|
||||||
var fuel = 0;
|
|
||||||
for (inst in instructions) {
|
|
||||||
if (inst == Forward || inst == Back || inst == Up || inst == Down) {
|
|
||||||
fuel++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return fuel;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getRequiredBlocks():Int {
|
|
||||||
var blocks = 0;
|
|
||||||
for (inst in instructions) {
|
|
||||||
switch inst {
|
|
||||||
case Place(_):
|
|
||||||
blocks++;
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return blocks;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Return the offset of the turtle after executing the instructions.
|
|
||||||
The origin is the starting position of the turtle.
|
|
||||||
**/
|
|
||||||
public function getFinalOffset():{offset:Pos3, faceing:Pos} {
|
|
||||||
var pos:Pos3 = {x: 0, y: 0, z: 0};
|
|
||||||
var forwardVec:Pos3 = {x: 1, y: 0, z: 0};
|
|
||||||
|
|
||||||
for (inst in instructions) {
|
|
||||||
switch inst {
|
|
||||||
case Forward:
|
|
||||||
pos = pos + forwardVec;
|
|
||||||
case Back:
|
|
||||||
pos = pos - forwardVec;
|
|
||||||
case TurnRight:
|
|
||||||
forwardVec = {x: -forwardVec.z, z: forwardVec.x, y: forwardVec.y};
|
|
||||||
case TurnLeft:
|
|
||||||
forwardVec = {x: forwardVec.z, z: -forwardVec.x, y: forwardVec.y};
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {offset: pos, faceing: new Pos({x: forwardVec.x, y: forwardVec.z})};
|
|
||||||
}
|
|
||||||
|
|
||||||
public function execute() {
|
|
||||||
for (inst in instructions) {
|
|
||||||
executeInst(inst);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function executeInst(instruction:TurtleInstruction):Outcome<Noise, String> {
|
|
||||||
switch instruction {
|
|
||||||
case Forward:
|
|
||||||
return Turtle.forward();
|
|
||||||
case Back:
|
|
||||||
return Turtle.back();
|
|
||||||
case Up:
|
|
||||||
return Turtle.up();
|
|
||||||
case Down:
|
|
||||||
return Turtle.down();
|
|
||||||
case TurnLeft:
|
|
||||||
return Turtle.turnLeft();
|
|
||||||
case TurnRight:
|
|
||||||
return Turtle.turnRight();
|
|
||||||
case Dig(dir):
|
|
||||||
return Turtle.dig(dir);
|
|
||||||
case Place(dir):
|
|
||||||
return Turtle.place(dir);
|
|
||||||
case PlacseSign(dir, text):
|
|
||||||
return Turtle.placeSign(dir, text);
|
|
||||||
case Select(slot):
|
|
||||||
var r = Turtle.selectSlot(slot);
|
|
||||||
if (r.isSuccess()) {
|
|
||||||
return Outcome.Success(null);
|
|
||||||
} else {
|
|
||||||
return Outcome.Failure("Failed to select slot");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Outcome.Failure("Unknown instruction: " + instruction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
package lib.turtle;
|
|
||||||
|
|
||||||
import kernel.turtle.Turtle;
|
|
||||||
|
|
||||||
using tink.CoreApi;
|
|
||||||
|
|
||||||
/**
|
|
||||||
Extends the Turtle kernel.
|
|
||||||
**/
|
|
||||||
class TurtleExt {
|
|
||||||
public static function getFreeSlots(t:Turtle):Int {
|
|
||||||
var ret:Int = 0;
|
|
||||||
|
|
||||||
for (i in 0...Turtle.MAX_SLOTS) {
|
|
||||||
if (t.getItemCount(i) == 0) {
|
|
||||||
ret++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function canPickup(t:Turtle, item:Item):Bool {
|
|
||||||
for (i in 0...Turtle.MAX_SLOTS) {
|
|
||||||
var slotItem = t.getItemDetail(i).orNull();
|
|
||||||
if (slotItem != null && slotItem.name == item) {
|
|
||||||
if (t.getItemSpace(i) > 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function canPickupCount(t:Turtle, item:Item):Int {
|
|
||||||
var ret:Int = 0;
|
|
||||||
|
|
||||||
for (i in 0...Turtle.MAX_SLOTS) {
|
|
||||||
var slotItem = Turtle.getItemDetail(i).orNull();
|
|
||||||
if (slotItem != null && slotItem.name == item) {
|
|
||||||
ret += Turtle.getItemSpace(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getInventory(t:Turtle):Map<Int, {item:Item, count:Int, free:Int}> {
|
|
||||||
var rtn:Map<Int, {item:Item, count:Int, free:Int}> = new Map();
|
|
||||||
|
|
||||||
for (i in 0...Turtle.MAX_SLOTS) {
|
|
||||||
var slotItem = t.getItemDetail(i).orNull();
|
|
||||||
if (slotItem != null) {
|
|
||||||
rtn.set(i, {item: slotItem.name, count: slotItem.count, free: t.getItemSpace(i)});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rtn;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Optimize the turtle inventory.
|
|
||||||
TODO: does not work 100% yet. Can be optimized more i think. Not that it needs to be optimized.
|
|
||||||
**/
|
|
||||||
public static function tidy(t:Turtle) {
|
|
||||||
var inv = getInventory(t);
|
|
||||||
// For each item in the inventory
|
|
||||||
for (k1 => v1 in inv) {
|
|
||||||
if (v1.free == 0 || v1.count == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (k2 => v2 in inv) {
|
|
||||||
if (k2 == k1) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (v1.item == v2.item && v2.free > 0) {
|
|
||||||
// We can move an item in there
|
|
||||||
t.transfer(k1, k2, v2.free);
|
|
||||||
|
|
||||||
v1.count -= v2.free;
|
|
||||||
if (v1.count <= 0) {
|
|
||||||
inv.remove(k1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
package lib.turtle;
|
|
||||||
|
|
||||||
import kernel.turtle.Types.InteractDirections;
|
|
||||||
import kernel.turtle.Types.TurtleSlot;
|
|
||||||
|
|
||||||
enum TurtleInstruction {
|
|
||||||
Forward;
|
|
||||||
Back;
|
|
||||||
Up;
|
|
||||||
Down;
|
|
||||||
TurnLeft;
|
|
||||||
TurnRight;
|
|
||||||
Dig(dir:InteractDirections);
|
|
||||||
Place(dir:InteractDirections);
|
|
||||||
PlacseSign(dir:InteractDirections, text:String);
|
|
||||||
Select(slot:TurtleSlot);
|
|
||||||
}
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
package lib.turtle;
|
|
||||||
|
|
||||||
// Check usage of NativeStringTools
|
|
||||||
// here https://api.haxe.org/lua/NativeStringTools.html
|
|
||||||
// and here http://lua-users.org/wiki/StringLibraryTutorial
|
|
||||||
import kernel.turtle.Types.TurtleSlot;
|
|
||||||
import kernel.turtle.Turtle;
|
|
||||||
import lua.NativeStringTools;
|
|
||||||
|
|
||||||
using tink.CoreApi;
|
|
||||||
|
|
||||||
/**
|
|
||||||
Save a set of turtle instructions to a string and execute them.
|
|
||||||
**/
|
|
||||||
class TurtleInstructionParser {
|
|
||||||
public function new() {}
|
|
||||||
|
|
||||||
public function encode(instructions:Array<TurtleInstruction>):String {
|
|
||||||
var s = "";
|
|
||||||
|
|
||||||
var times = 0;
|
|
||||||
var lastInstruction:TurtleInstruction = null;
|
|
||||||
|
|
||||||
for (inst in instructions) {
|
|
||||||
if (inst == lastInstruction) {
|
|
||||||
times++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lastInstruction != null) {
|
|
||||||
var encoded = encodeInstruction(lastInstruction);
|
|
||||||
s = s + '${times + 1}$encoded';
|
|
||||||
}
|
|
||||||
|
|
||||||
times = 0;
|
|
||||||
lastInstruction = inst;
|
|
||||||
}
|
|
||||||
|
|
||||||
var encoded = encodeInstruction(lastInstruction);
|
|
||||||
s = s + '${times + 1}$encoded';
|
|
||||||
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function encodeInstruction(inst:TurtleInstruction):String {
|
|
||||||
for (k => v in cmdMap) {
|
|
||||||
if (v == inst) {
|
|
||||||
return k;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch inst {
|
|
||||||
case Select(slot):
|
|
||||||
return "m" + encodeSlot(slot);
|
|
||||||
default:
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
private function encodeSlot(slot:TurtleSlot):String {
|
|
||||||
return String.fromCharCode(slot + 97);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function parse(instructionsString:String):Array<TurtleInstruction> {
|
|
||||||
var rtn:Array<TurtleInstruction> = [];
|
|
||||||
var mfunc = NativeStringTools.gmatch(instructionsString, "%d+%D+");
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
var found = mfunc();
|
|
||||||
|
|
||||||
if (found == null) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
var times = Std.parseInt(NativeStringTools.match(found, "%d+"));
|
|
||||||
var command = NativeStringTools.match(found, "%D+");
|
|
||||||
|
|
||||||
var cmd = cmdMap.get(command);
|
|
||||||
if (cmd != null) {
|
|
||||||
rtn = rtn.concat([for (i in 0...times) cmd]);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch command.charAt(0) {
|
|
||||||
case "m": // select
|
|
||||||
var slot = parseSlot(command.charAt(1));
|
|
||||||
rtn = rtn.concat([for (i in 0...times) Select(slot)]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rtn;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function parseSlot(slot:String):TurtleSlot {
|
|
||||||
var slot = slot.charCodeAt(0) - 97;
|
|
||||||
|
|
||||||
if (slot < 0 || slot > Turtle.MAX_SLOTS) {
|
|
||||||
// TODO: better error handling
|
|
||||||
throw new Error("Invalid slot: " + slot);
|
|
||||||
}
|
|
||||||
|
|
||||||
return slot;
|
|
||||||
}
|
|
||||||
|
|
||||||
private var cmdMap:Map<String, TurtleInstruction> = [
|
|
||||||
"a" => Forward, "b" => Back, "c" => TurnLeft, "d" => TurnRight, "e" => Up, "f" => Down, "g" => Dig(Front), "h" => Dig(Up), "i" => Dig(Down),
|
|
||||||
"j" => Place(Front), "k" => Place(Up), "l" => Place(Down),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user