ArithmeticEngine successfully working!

This commit is contained in:
Talia-12 2023-05-31 17:49:22 +10:00
parent c276d4c600
commit 6f5c652554
16 changed files with 305 additions and 80 deletions

View file

@ -0,0 +1,43 @@
package at.petrak.hexcasting.api.casting.arithmetic;
import org.apache.commons.lang3.function.TriFunction;
import org.jetbrains.annotations.NotNull;
import java.util.Iterator;
public class TripleIterable<A,B,C,D> implements Iterable<D> {
private final Iterable<A> iterableA;
private final Iterable<B> iterableB;
private final Iterable<C> iterableC;
private final TriFunction<A, B, C, D> map;
public TripleIterable(Iterable<A> iterableA, Iterable<B> iterableB, Iterable<C> iterableC, TriFunction<A, B, C, D> map) {
this.iterableA = iterableA;
this.iterableB = iterableB;
this.iterableC = iterableC;
this.map = map;
}
@NotNull
@Override
public Iterator<D> iterator() {
return new TripleIterator();
}
class TripleIterator implements Iterator<D> {
private final Iterator<A> iteratorA = iterableA.iterator();
private final Iterator<B> iteratorB = iterableB.iterator();
private final Iterator<C> iteratorC = iterableC.iterator();
@Override
public boolean hasNext() {
return iteratorA.hasNext() && iteratorB.hasNext() && iteratorC.hasNext();
}
@Override
public D next() {
return map.apply(iteratorA.next(), iteratorB.next(), iteratorC.next());
}
}
}

View file

@ -2,8 +2,17 @@ package at.petrak.hexcasting.api.casting.arithmetic.engine;
import at.petrak.hexcasting.api.casting.arithmetic.Arithmetic;
import at.petrak.hexcasting.api.casting.arithmetic.operator.Operator;
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment;
import at.petrak.hexcasting.api.casting.eval.OperationResult;
import at.petrak.hexcasting.api.casting.eval.vm.CastingImage;
import at.petrak.hexcasting.api.casting.eval.vm.SpellContinuation;
import at.petrak.hexcasting.api.casting.iota.Iota;
import at.petrak.hexcasting.api.casting.math.HexPattern;
import at.petrak.hexcasting.api.casting.mishaps.Mishap;
import at.petrak.hexcasting.api.casting.mishaps.MishapInvalidOperatorArgs;
import at.petrak.hexcasting.api.casting.mishaps.MishapNotEnoughArgs;
import at.petrak.hexcasting.common.lib.hex.HexEvalSounds;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
@ -42,15 +51,33 @@ public class ArithmeticEngine {
return operators.keySet();
}
public @Nullable Iota run(HexPattern operator, Stack<Iota> iotas) {
@Nullable
public OperationResult operate(@NotNull HexPattern operator, @NotNull CastingEnvironment env, @NotNull CastingImage image, @NotNull SpellContinuation continuation) throws Mishap {
var stackList = image.getStack();
var stack = new Stack<Iota>();
stack.addAll(stackList);
var startingLength = stackList.size();
try {
var ret = run(operator, stack, startingLength);
ret.forEach(stack::add);
var image2 = image.copy(stack, image.getParenCount(), image.getParenthesized(), image.getEscapeNext(), image.getOpsConsumed() + 1, image.getUserData());
return new OperationResult(image2, List.of(), continuation, HexEvalSounds.NORMAL_EXECUTE);
} catch (InvalidOperatorException e) {
return null;
} catch (NoOperatorCandidatesException e) {
throw new MishapInvalidOperatorArgs(e.args, e.pattern);
}
}
public Iterable<Iota> run(HexPattern operator, Stack<Iota> iotas, int startingLength) throws Mishap {
var candidates = operators.get(operator);
if (candidates == null)
return null; // not an operator
throw new InvalidOperatorException("the pattern " + operator + " is not an operator."); //
HashCons hash = new HashCons.Pattern(operator);
var args = new ArrayList<Iota>(candidates.arity());
for (var i = 0; i < candidates.arity(); i++) {
if (iotas.isEmpty()) {
throw new IllegalStateException("Not enough args on stack for operator: " + operator);
throw new MishapNotEnoughArgs(candidates.arity, startingLength);
}
var iota = iotas.pop();
hash = new HashCons.Pair(iota.getType(), hash);
@ -68,7 +95,7 @@ public class ArithmeticEngine {
return op;
}
}
throw new IllegalArgumentException("No implementation candidates for op " + candidates.pattern() + " on args: " + args);
throw new NoOperatorCandidatesException(candidates.pattern(), args, "No implementation candidates for op " + candidates.pattern() + " on args: " + args);
});
}
}

View file

@ -0,0 +1,18 @@
package at.petrak.hexcasting.api.casting.arithmetic.engine;
public class InvalidOperatorException extends RuntimeException {
public InvalidOperatorException() {
}
public InvalidOperatorException(String s) {
super(s);
}
public InvalidOperatorException(String message, Throwable cause) {
super(message, cause);
}
public InvalidOperatorException(Throwable cause) {
super(cause);
}
}

View file

@ -0,0 +1,23 @@
package at.petrak.hexcasting.api.casting.arithmetic.engine;
import at.petrak.hexcasting.api.casting.iota.Iota;
import at.petrak.hexcasting.api.casting.math.HexPattern;
import java.util.List;
public class NoOperatorCandidatesException extends RuntimeException {
HexPattern pattern;
List<Iota> args;
public NoOperatorCandidatesException(HexPattern pattern, List<Iota> args) {
this.pattern = pattern;
this.args = args;
}
public NoOperatorCandidatesException(HexPattern pattern, List<Iota> args, String s) {
super(s);
this.pattern = pattern;
this.args = args;
}
}

View file

@ -7,16 +7,14 @@ import at.petrak.hexcasting.api.casting.arithmetic.operator.Operator;
import at.petrak.hexcasting.api.casting.arithmetic.operator.OperatorBinary;
import at.petrak.hexcasting.api.casting.arithmetic.operator.OperatorUnary;
import at.petrak.hexcasting.api.casting.iota.DoubleIota;
import at.petrak.hexcasting.api.casting.iota.Iota;
import at.petrak.hexcasting.api.casting.math.HexPattern;
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
import java.util.List;
import java.util.function.DoubleBinaryOperator;
import java.util.function.DoubleUnaryOperator;
import static at.petrak.hexcasting.api.casting.arithmetic.operator.Operator.downcast;
import static at.petrak.hexcasting.common.lib.hex.HexIotaTypes.*;
import static at.petrak.hexcasting.common.lib.hex.HexIotaTypes.DOUBLE;
public enum DoubleArithmetic implements Arithmetic {
INSTANCE;
@ -47,16 +45,24 @@ public enum DoubleArithmetic implements Arithmetic {
@Override
public Operator getOperator(HexPattern pattern) {
switch (pattern) {
case ADD: return make2(Double::sum);
case SUB: return make2((p, q) -> p - q);
case MUL: return make2((p, q) -> p * q);
case DIV: return make2((p, q) -> p / q);
case ABS: return make1(Math::abs);
case POW: return make2(Math::pow);
case FLOOR: return make1(Math::floor);
case CEIL: return make1(Math::ceil);
case MOD: return make2((p, q) -> p % q);
if (pattern.equals(ADD)) {
return make2(Double::sum);
} else if (pattern.equals(SUB)) {
return make2((p, q) -> p - q);
} else if (pattern.equals(MUL)) {
return make2((p, q) -> p * q);
} else if (pattern.equals(DIV)) {
return make2((p, q) -> p / q);
} else if (pattern.equals(ABS)) {
return make1(Math::abs);
} else if (pattern.equals(POW)) {
return make2(Math::pow);
} else if (pattern.equals(FLOOR)) {
return make1(Math::floor);
} else if (pattern.equals(CEIL)) {
return make1(Math::ceil);
} else if (pattern.equals(MOD)) {
return make2((p, q) -> p % q);
}
return null;
}

View file

@ -3,11 +3,18 @@ package at.petrak.hexcasting.api.casting.arithmetic.impls;
import at.petrak.hexcasting.api.casting.arithmetic.Arithmetic;
import at.petrak.hexcasting.api.casting.arithmetic.IotaMultiPredicate;
import at.petrak.hexcasting.api.casting.arithmetic.IotaPredicate;
import at.petrak.hexcasting.api.casting.arithmetic.operator.*;
import at.petrak.hexcasting.api.casting.iota.DoubleIota;
import at.petrak.hexcasting.api.casting.iota.Vec3Iota;
import at.petrak.hexcasting.api.casting.math.HexPattern;
import net.minecraft.world.phys.Vec3;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;
import static at.petrak.hexcasting.api.casting.arithmetic.operator.Operator.downcast;
import static at.petrak.hexcasting.common.lib.hex.HexIotaTypes.*;
public enum Vec3Arithmetic implements Arithmetic {
@ -31,28 +38,43 @@ public enum Vec3Arithmetic implements Arithmetic {
}
@Override
public Iterable<Symbol> opTypes() {
public Iterable<HexPattern> opTypes() {
return OPS;
}
@Override
public Operator getOperator(Symbol name) {
switch (name.inner()) {
case "pack": return OperatorPack.INSTANCE;
case "add": return make2(name, null);
case "sub": return make2(name, null);
case "mul": return make2(name, Vec3::dot);
case "div": return make2(name, Vec3::cross);
case "abs": return make1(Vec3::len);
case "pow": return make2(name, Vec3::proj);
case "mod": return make2(name, null);
public Operator getOperator(HexPattern pattern) {
if (pattern.equals(PACK)) {
return OperatorPack.INSTANCE;
} else if (pattern.equals(UNPACK)) {
return OperatorUnpack.INSTANCE;
} else if (pattern.equals(ADD)) {
return make2Fallback(pattern);
} else if (pattern.equals(SUB)) {
return make2Fallback(pattern);
} else if (pattern.equals(MUL)) {
return make2Double(pattern, Vec3::dot);
} else if (pattern.equals(DIV)) {
return make2Vec(pattern, Vec3::cross);
} else if (pattern.equals(ABS)) {
return make1Double(Vec3::length);
} else if (pattern.equals(POW)) {
return make2Vec(pattern, (u, v) -> v.normalize().scale(u.dot(v.normalize())));
} else if (pattern.equals(MOD)) {
return make2Fallback(pattern);
}
return null;
}
public static OperatorUnary make1(Function<Vec3, Object> op) {
return new OperatorUnary(ACCEPTS, i -> new Iota(op.apply(i.downcast(Vec3.class))));
public static OperatorUnary make1Double(Function<Vec3, Double> op) {
return new OperatorUnary(ACCEPTS, i -> new DoubleIota(op.apply(downcast(i, VEC3).getVec3())));
}
public static OperatorVec3Delegating make2(Symbol name, BiFunction<Vec3, Vec3, Object> op) {
return new OperatorVec3Delegating(op, name);
public static OperatorVec3Delegating make2Fallback(HexPattern pattern) {
return new OperatorVec3Delegating(null, pattern);
}
public static OperatorVec3Delegating make2Double(HexPattern pattern, BiFunction<Vec3, Vec3, Double> op) {
return new OperatorVec3Delegating(op.andThen(DoubleIota::new), pattern);
}
public static OperatorVec3Delegating make2Vec(HexPattern pattern, BiFunction<Vec3, Vec3, Vec3> op) {
return new OperatorVec3Delegating(op.andThen(Vec3Iota::new), pattern);
}
}

View file

@ -3,7 +3,6 @@ package at.petrak.hexcasting.api.casting.arithmetic.operator;
import at.petrak.hexcasting.api.casting.arithmetic.IotaMultiPredicate;
import at.petrak.hexcasting.api.casting.iota.Iota;
import at.petrak.hexcasting.api.casting.iota.IotaType;
import at.petrak.hexcasting.api.casting.mishaps.MishapInvalidIota;
public abstract class Operator {
public final int arity;
@ -15,7 +14,7 @@ public abstract class Operator {
this.accepts = accepts;
}
public abstract Iota apply(Iterable<Iota> iotas);
public abstract Iterable<Iota> apply(Iterable<Iota> iotas);
@SuppressWarnings("unchecked")
public static <T extends Iota> T downcast(Iota iota, IotaType<T> iotaType) {

View file

@ -4,6 +4,7 @@ package at.petrak.hexcasting.api.casting.arithmetic.operator;
import at.petrak.hexcasting.api.casting.arithmetic.IotaMultiPredicate;
import at.petrak.hexcasting.api.casting.iota.Iota;
import java.util.List;
import java.util.function.BinaryOperator;
public class OperatorBinary extends Operator {
@ -15,8 +16,8 @@ public class OperatorBinary extends Operator {
}
@Override
public Iota apply(Iterable<Iota> iotas) {
public Iterable<Iota> apply(Iterable<Iota> iotas) {
var it = iotas.iterator();
return inner.apply(it.next(), it.next());
return List.of(inner.apply(it.next(), it.next()));
}
}

View file

@ -8,6 +8,8 @@ import at.petrak.hexcasting.api.casting.iota.Vec3Iota;
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
import net.minecraft.world.phys.Vec3;
import java.util.List;
public class OperatorPack extends Operator {
private OperatorPack() {
super(3, IotaMultiPredicate.all(IotaPredicate.ofType(HexIotaTypes.DOUBLE)));
@ -16,12 +18,12 @@ public class OperatorPack extends Operator {
public static OperatorPack INSTANCE = new OperatorPack();
@Override
public Iota apply(Iterable<Iota> iotas) {
public Iterable<Iota> apply(Iterable<Iota> iotas) {
var it = iotas.iterator();
return new Vec3Iota(new Vec3(
return List.of(new Vec3Iota(new Vec3(
downcast(it.next(), HexIotaTypes.DOUBLE).getDouble(),
downcast(it.next(), HexIotaTypes.DOUBLE).getDouble(),
downcast(it.next(), HexIotaTypes.DOUBLE).getDouble()
));
)));
}
}

View file

@ -4,6 +4,7 @@ package at.petrak.hexcasting.api.casting.arithmetic.operator;
import at.petrak.hexcasting.api.casting.arithmetic.IotaMultiPredicate;
import at.petrak.hexcasting.api.casting.iota.Iota;
import java.util.List;
import java.util.function.UnaryOperator;
public class OperatorUnary extends Operator {
@ -15,7 +16,7 @@ public class OperatorUnary extends Operator {
}
@Override
public Iota apply(Iterable<Iota> iotas) {
return inner.apply(iotas.iterator().next());
public Iterable<Iota> apply(Iterable<Iota> iotas) {
return List.of(inner.apply(iotas.iterator().next()));
}
}

View file

@ -0,0 +1,27 @@
package at.petrak.hexcasting.api.casting.arithmetic.operator;
import at.petrak.hexcasting.api.casting.arithmetic.IotaMultiPredicate;
import at.petrak.hexcasting.api.casting.arithmetic.IotaPredicate;
import at.petrak.hexcasting.api.casting.iota.DoubleIota;
import at.petrak.hexcasting.api.casting.iota.Iota;
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
import java.util.List;
import static at.petrak.hexcasting.common.lib.hex.HexIotaTypes.VEC3;
public class OperatorUnpack extends Operator {
private OperatorUnpack() {
super(1, IotaMultiPredicate.all(IotaPredicate.ofType(HexIotaTypes.DOUBLE)));
}
public static OperatorUnpack INSTANCE = new OperatorUnpack();
@Override
public Iterable<Iota> apply(Iterable<Iota> iotas) {
var it = iotas.iterator();
var vec = downcast(it.next(), VEC3).getVec3();
return List.of(new DoubleIota(vec.x), new DoubleIota(vec.y), new DoubleIota(vec.z));
}
}

View file

@ -3,42 +3,46 @@ package at.petrak.hexcasting.api.casting.arithmetic.operator;
import at.petrak.hexcasting.api.casting.arithmetic.IotaMultiPredicate;
import at.petrak.hexcasting.api.casting.arithmetic.IotaPredicate;
import at.petrak.hexcasting.api.casting.arithmetic.IterPair;
import at.petrak.hexcasting.api.casting.arithmetic.TripleIterable;
import at.petrak.hexcasting.api.casting.arithmetic.impls.DoubleArithmetic;
import at.petrak.hexcasting.api.casting.iota.DoubleIota;
import at.petrak.hexcasting.api.casting.iota.Iota;
import at.petrak.hexcasting.api.casting.iota.Vec3Iota;
import at.petrak.hexcasting.api.casting.math.HexPattern;
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
import kotlin.Pair;
import net.minecraft.world.phys.Vec3;
import java.util.List;
import java.util.Objects;
import java.util.function.BiFunction;
import static at.petrak.hexcasting.common.lib.hex.HexIotaTypes.DOUBLE;
import static at.petrak.hexcasting.common.lib.hex.HexIotaTypes.VEC3;
public class OperatorVec3Delegating extends Operator {
private final BiFunction<Vec3, Vec3, Iota> op;
private final Operator fb;
public OperatorVec3Delegating(BiFunction<Vec3, Vec3, Iota> core, HexPattern fallback) {
super(2, IotaMultiPredicate.any(IotaPredicate.ofType(HexIotaTypes.VEC3), IotaPredicate.ofType(HexIotaTypes.DOUBLE)));
super(2, IotaMultiPredicate.any(IotaPredicate.ofType(VEC3), IotaPredicate.ofType(DOUBLE)));
op = core;
fb = Objects.requireNonNull(DoubleArithmetic.INSTANCE.getOperator(fallback));
}
@Override
public Iota apply(Iterable<Iota> iotas) {
public Iterable<Iota> apply(Iterable<Iota> iotas) {
var it = iotas.iterator();
var left = it.next();
var right = it.next();
if (op != null && left instanceof Vec3Iota lh && right instanceof Vec3Iota rh) {
return op.apply(lh.getVec3(), rh.getVec3());
return List.of(op.apply(lh.getVec3(), rh.getVec3()));
}
var lh = left instanceof Vec3Iota l ? l.getVec3() : triplicate(downcast(left, HexIotaTypes.DOUBLE).getDouble());
var rh = right instanceof Vec3Iota r ? r.getVec3() : triplicate(downcast(right, HexIotaTypes.DOUBLE).getDouble());
return new Vec3Iota(new Vec3(
downcast(fb.apply(new IterPair<>(new DoubleIota(lh.x()), new DoubleIota(rh.x()))), HexIotaTypes.DOUBLE).getDouble(),
downcast(fb.apply(new IterPair<>(new DoubleIota(lh.y()), new DoubleIota(rh.y()))), HexIotaTypes.DOUBLE).getDouble(),
downcast(fb.apply(new IterPair<>(new DoubleIota(lh.z()), new DoubleIota(rh.z()))), HexIotaTypes.DOUBLE).getDouble()
));
var lh = left instanceof Vec3Iota l ? l.getVec3() : triplicate(downcast(left, DOUBLE).getDouble());
var rh = right instanceof Vec3Iota r ? r.getVec3() : triplicate(downcast(right, DOUBLE).getDouble());
return new TripleIterable<>(
fb.apply(new IterPair<>(new DoubleIota(lh.x()), new DoubleIota(rh.x()))),
fb.apply(new IterPair<>(new DoubleIota(lh.y()), new DoubleIota(rh.y()))),
fb.apply(new IterPair<>(new DoubleIota(lh.z()), new DoubleIota(rh.z()))),
(x, y, z) -> new Vec3Iota(new Vec3(downcast(x, DOUBLE).getDouble(), downcast(y, DOUBLE).getDouble(), downcast(y, DOUBLE).getDouble()))
);
}
public static Vec3 triplicate(double in) {

View file

@ -97,4 +97,9 @@ public abstract class Iota {
public static boolean tolerates(Iota a, Iota b) {
return a.toleratesOther(b) || b.toleratesOther(a);
}
@Override
public int hashCode() {
return payload.hashCode();
}
}

View file

@ -17,6 +17,7 @@ import at.petrak.hexcasting.api.mod.HexConfig;
import at.petrak.hexcasting.api.mod.HexTags;
import at.petrak.hexcasting.api.utils.HexUtils;
import at.petrak.hexcasting.common.casting.PatternRegistryManifest;
import at.petrak.hexcasting.common.lib.hex.HexArithmetics;
import at.petrak.hexcasting.common.lib.hex.HexEvalSounds;
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
import at.petrak.hexcasting.xplat.IXplatAbstractions;
@ -68,6 +69,9 @@ public class PatternIota extends Iota {
public @NotNull CastResult execute(CastingVM vm, ServerLevel world, SpellContinuation continuation) {
@Nullable Component castedName = null;
try {
var result = HexArithmetics.ENGINE.operate(this.getPattern(), vm.getEnv(), vm.getImage(), continuation);
if (result == null) {
var lookup = PatternRegistryManifest.matchPattern(this.getPattern(), world, false);
vm.getEnv().precheckAction(lookup);
@ -95,11 +99,13 @@ public class PatternIota extends Iota {
} else throw new IllegalStateException();
// do the actual calculation!!
var result = action.operate(
result = action.operate(
vm.getEnv(),
vm.getImage(),
continuation
);
}
if (result.getNewImage().getOpsConsumed() > HexConfig.server().maxOpCount()) {
throw new MishapEvalTooMuch();
}

View file

@ -0,0 +1,32 @@
package at.petrak.hexcasting.api.casting.mishaps
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
import at.petrak.hexcasting.api.casting.iota.GarbageIota
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.casting.math.HexPattern
import at.petrak.hexcasting.api.pigment.FrozenPigment
import net.minecraft.network.chat.ComponentContents
import net.minecraft.network.chat.MutableComponent
import net.minecraft.world.item.DyeColor
/**
* The value failed some kind of predicate.
*/
class MishapInvalidOperatorArgs(
val perpetrators: List<Iota>,
val operator: HexPattern
) : Mishap() {
override fun accentColor(ctx: CastingEnvironment, errorCtx: Context): FrozenPigment =
dyeColor(DyeColor.GRAY)
override fun execute(ctx: CastingEnvironment, errorCtx: Context, stack: MutableList<Iota>) {
for (i in perpetrators.indices) {
stack[stack.size - 1 - i] = GarbageIota()
}
}
override fun errorMessage(ctx: CastingEnvironment, errorCtx: Context) =
error(
"invalid_operator_args", operator, perpetrators.fold(MutableComponent.create(ComponentContents.EMPTY)) { mc, iota -> mc.append(iota.display()) }
)
}

View file

@ -0,0 +1,9 @@
package at.petrak.hexcasting.common.lib.hex;
import at.petrak.hexcasting.api.casting.arithmetic.engine.ArithmeticEngine;
import at.petrak.hexcasting.api.casting.arithmetic.impls.DoubleArithmetic;
import at.petrak.hexcasting.api.casting.arithmetic.impls.Vec3Arithmetic;
public class HexArithmetics {
public static ArithmeticEngine ENGINE = new ArithmeticEngine(DoubleArithmetic.INSTANCE, Vec3Arithmetic.INSTANCE);
}