Barely functional logic

This commit is contained in:
Anuken
2020-08-07 18:30:03 -04:00
parent 090e0f35dc
commit 51af6eeec9
8 changed files with 143 additions and 37 deletions

View File

@@ -18,6 +18,7 @@ import mindustry.world.blocks.environment.*;
import mindustry.world.blocks.experimental.*; import mindustry.world.blocks.experimental.*;
import mindustry.world.blocks.legacy.*; import mindustry.world.blocks.legacy.*;
import mindustry.world.blocks.liquid.*; import mindustry.world.blocks.liquid.*;
import mindustry.world.blocks.logic.*;
import mindustry.world.blocks.power.*; import mindustry.world.blocks.power.*;
import mindustry.world.blocks.production.*; import mindustry.world.blocks.production.*;
import mindustry.world.blocks.sandbox.*; import mindustry.world.blocks.sandbox.*;
@@ -1886,7 +1887,7 @@ public class Blocks implements ContentList{
//endregion campaign //endregion campaign
//region experimental //region experimental
logicProcessor = new ResearchBlock("logic-processor"){{ logicProcessor = new LogicProcessor("logic-processor"){{
requirements(Category.effect, BuildVisibility.debugOnly, with(Items.copper, 200, Items.lead, 100)); requirements(Category.effect, BuildVisibility.debugOnly, with(Items.copper, 200, Items.lead, 100));
size = 3; size = 3;

View File

@@ -1,7 +1,6 @@
package mindustry.logic; package mindustry.logic;
import arc.struct.*; import arc.struct.*;
import arc.util.ArcAnnotate.*;
import mindustry.io.*; import mindustry.io.*;
import mindustry.logic.LCanvas.*; import mindustry.logic.LCanvas.*;
import mindustry.logic.LExecutor.*; import mindustry.logic.LExecutor.*;
@@ -13,8 +12,6 @@ public class LAssembler{
ObjectMap<String, BVar> vars = new ObjectMap<>(); ObjectMap<String, BVar> vars = new ObjectMap<>();
/** All instructions to be executed. */ /** All instructions to be executed. */
LInstruction[] instructions; LInstruction[] instructions;
/** Statement UI elements being processed. */
@Nullable Seq<StatementElem> elements;
public LAssembler(){ public LAssembler(){
//add default constants //add default constants
@@ -26,35 +23,42 @@ public class LAssembler{
public static LAssembler assemble(Seq<StatementElem> seq){ public static LAssembler assemble(Seq<StatementElem> seq){
LAssembler out = new LAssembler(); LAssembler out = new LAssembler();
out.elements = seq; seq.each(s -> s.st.saveUI());
out.instructions = seq.map(s -> s.st.build(out)).toArray(LInstruction.class); out.instructions = seq.map(s -> s.st.build(out)).toArray(LInstruction.class);
return out; return out;
} }
public static String toJson(Seq<StatementElem> seq){
seq.each(s -> s.st.saveUI());
LStatement[] statements = seq.map(s -> s.st).toArray(LStatement.class);
return JsonIO.write(statements);
}
//TODO this is awful and confusing //TODO this is awful and confusing
public static LAssembler fromJson(String json){ public static LAssembler fromJson(String json){
LAssembler asm = new LAssembler(); LAssembler asm = new LAssembler();
LStatement[] statements = JsonIO.read(LStatement[].class, json); LStatement[] statements = JsonIO.read(LStatement[].class, json);
for(LStatement s : statements){ for(LStatement s : statements){
s.afterLoad(asm); s.setupUI();
} }
asm.instructions = Seq.with(statements).map(l -> l.build(asm)).toArray(LInstruction.class); asm.instructions = Seq.with(statements).map(l -> l.build(asm)).toArray(LInstruction.class);
return asm; return asm;
} }
public String toJson(){
Seq<LStatement> states = elements.map(s -> s.st);
states.each(s -> s.beforeSave(this));
return JsonIO.write(states.toArray(LStatement.class));
}
/** @return a variable ID by name. /** @return a variable ID by name.
* This may be a constant variable referring to a number or object. */ * This may be a constant variable referring to a number or object. */
public int var(String symbol){ public int var(String symbol){
symbol = symbol.trim(); symbol = symbol.trim();
//string case
if(symbol.startsWith("\"") && symbol.endsWith("\"")){
return putConst("___" + symbol, symbol.substring(1, symbol.length() - 1)).id;
}
try{ try{
double value = Double.parseDouble(symbol); double value = Double.parseDouble(symbol);
//this creates a hidden const variable with the specified value //this creates a hidden const variable with the specified value
@@ -66,7 +70,7 @@ public class LAssembler{
} }
/** Adds a constant value by name. */ /** Adds a constant value by name. */
private BVar putConst(String name, Object value){ BVar putConst(String name, Object value){
BVar var = putVar(name); BVar var = putVar(name);
var.constant = true; var.constant = true;
var.value = value; var.value = value;
@@ -74,7 +78,7 @@ public class LAssembler{
} }
/** Registers a variable name mapping. */ /** Registers a variable name mapping. */
private BVar putVar(String name){ BVar putVar(String name){
if(vars.containsKey(name)){ if(vars.containsKey(name)){
return vars.get(name); return vars.get(name);
}else{ }else{

View File

@@ -35,11 +35,8 @@ public class LCanvas extends Table{
pane(statements).grow().get().setClip(false); pane(statements).grow().get().setClip(false);
add(new PrintStatement());
add(new AssignStatement()); add(new AssignStatement());
add(new FetchBuildStatement());
add(new JumpStatement());
add(new ToggleStatement());
add(new OpStatement());
} }
private void drawGrid(){ private void drawGrid(){
@@ -73,7 +70,7 @@ public class LCanvas extends Table{
} }
String save(){ String save(){
return LAssembler.assemble(statements.getChildren().as()).toJson(); return LAssembler.toJson(statements.getChildren().as());
} }
void load(String json){ void load(String json){
@@ -83,11 +80,8 @@ public class LCanvas extends Table{
add(st); add(st);
} }
LAssembler asm = new LAssembler();
asm.elements = this.statements.getChildren().as();
for(LStatement st : statements){ for(LStatement st : statements){
st.afterLoad(asm); st.setupUI();
} }
this.statements.layout(); this.statements.layout();
@@ -197,6 +191,7 @@ public class LCanvas extends Table{
public StatementElem(LStatement st){ public StatementElem(LStatement st){
this.st = st; this.st = st;
st.elem = this;
background(Tex.whitePane); background(Tex.whitePane);
setColor(st.category().color); setColor(st.category().color);

View File

@@ -1,10 +1,13 @@
package mindustry.logic; package mindustry.logic;
import arc.func.*;
import arc.scene.ui.layout.*; import arc.scene.ui.layout.*;
import arc.util.*;
import mindustry.ui.dialogs.*; import mindustry.ui.dialogs.*;
public class LDialog extends BaseDialog{ public class LDialog extends BaseDialog{
mindustry.logic.LCanvas canvas; LCanvas canvas;
Cons<String> consumer = s -> Log.info(s);
public LDialog(){ public LDialog(){
super("logic"); super("logic");
@@ -17,5 +20,16 @@ public class LDialog extends BaseDialog{
t.bottom(); t.bottom();
t.add(buttons); t.add(buttons);
})).grow(); })).grow();
hidden(() -> {
consumer.get(canvas.save());
});
}
public void show(String code, Cons<String> consumer){
canvas.load(code);
this.consumer = consumer;
show();
} }
} }

View File

@@ -1,6 +1,7 @@
package mindustry.logic; package mindustry.logic;
import arc.util.ArcAnnotate.*; import arc.util.ArcAnnotate.*;
import arc.util.*;
import mindustry.*; import mindustry.*;
import mindustry.gen.*; import mindustry.gen.*;
@@ -11,6 +12,10 @@ public class LExecutor{
//values of all the variables //values of all the variables
Var[] vars; Var[] vars;
public boolean initialized(){
return instructions != null && vars != null && instructions.length > 0;
}
/** Runs all the instructions at once. Debugging only. */ /** Runs all the instructions at once. Debugging only. */
public void runAll(){ public void runAll(){
counter = 0; counter = 0;
@@ -20,7 +25,9 @@ public class LExecutor{
} }
} }
public void load(LAssembler builder){ public void load(Object context, LAssembler builder){
builder.putConst("this", context);
vars = new Var[builder.vars.size]; vars = new Var[builder.vars.size];
instructions = builder.instructions; instructions = builder.instructions;
counter = 0; counter = 0;
@@ -58,6 +65,11 @@ public class LExecutor{
return v.isobj ? 0 : v.numval; return v.isobj ? 0 : v.numval;
} }
String str(int index){
Var v = vars[index];
return v.isobj ? String.valueOf(v.objval) : String.valueOf(v.numval);
}
int numi(int index){ int numi(int index){
return (int)num(index); return (int)num(index);
} }
@@ -101,6 +113,8 @@ public class LExecutor{
this.value = value; this.value = value;
} }
ToggleI(){}
@Override @Override
public void run(LExecutor exec){ public void run(LExecutor exec){
Building b = exec.building(target); Building b = exec.building(target);
@@ -125,6 +139,8 @@ public class LExecutor{
this.to = to; this.to = to;
} }
AssignI(){}
@Override @Override
public void run(LExecutor exec){ public void run(LExecutor exec){
Var v = exec.vars[to]; Var v = exec.vars[to];
@@ -144,7 +160,7 @@ public class LExecutor{
} }
public static class BinaryOpI implements LInstruction{ public static class BinaryOpI implements LInstruction{
public mindustry.logic.BinaryOp op; public BinaryOp op;
public int a, b, dest; public int a, b, dest;
public BinaryOpI(BinaryOp op, int a, int b, int dest){ public BinaryOpI(BinaryOp op, int a, int b, int dest){
@@ -154,6 +170,8 @@ public class LExecutor{
this.dest = dest; this.dest = dest;
} }
BinaryOpI(){}
@Override @Override
public void run(LExecutor exec){ public void run(LExecutor exec){
exec.setnum(dest, op.function.get(exec.num(a), exec.num(b))); exec.setnum(dest, op.function.get(exec.num(a), exec.num(b)));
@@ -168,6 +186,21 @@ public class LExecutor{
} }
} }
public static class PrintI implements LInstruction{
public int value;
public PrintI(int value){
this.value = value;
}
PrintI(){}
@Override
public void run(LExecutor exec){
Log.info(exec.str(value));
}
}
public static class JumpI implements LInstruction{ public static class JumpI implements LInstruction{
public int cond, to; public int cond, to;
@@ -176,6 +209,8 @@ public class LExecutor{
this.to = to; this.to = to;
} }
JumpI(){}
@Override @Override
public void run(LExecutor exec){ public void run(LExecutor exec){
if(to != -1 && exec.bool(cond)){ if(to != -1 && exec.bool(cond)){
@@ -194,6 +229,8 @@ public class LExecutor{
this.y = y; this.y = y;
} }
FetchBuildI(){}
@Override @Override
public void run(LExecutor exec){ public void run(LExecutor exec){
exec.setobj(dest, Vars.world.build(exec.numi(x), exec.numi(y))); exec.setobj(dest, Vars.world.build(exec.numi(x), exec.numi(y)));

View File

@@ -1,19 +1,23 @@
package mindustry.logic; package mindustry.logic;
import arc.scene.ui.layout.*; import arc.scene.ui.layout.*;
import arc.util.ArcAnnotate.*;
import mindustry.logic.LCanvas.*;
import mindustry.logic.LExecutor.*;
/** A statement is an intermediate representation of an instruction, to be used in UI. */ /** A statement is an intermediate representation of an instruction, to be used in UI. */
public abstract class LStatement{ public abstract class LStatement{
public transient @Nullable StatementElem elem;
public abstract void build(Table table); public abstract void build(Table table);
public abstract LCategory category(); public abstract LCategory category();
public abstract LExecutor.LInstruction build(LAssembler builder); public abstract LInstruction build(LAssembler builder);
public void afterLoad(LAssembler assembler){ public void setupUI(){
} }
public void beforeSave(LAssembler assembler){ public void saveUI(){
} }

View File

@@ -115,8 +115,29 @@ public class LStatements{
} }
} }
public static class PrintStatement extends LStatement{
public String value = "\"frog\"";
@Override
public void build(Table table){
table.field(value, Styles.nodeField, str -> value = str)
.size(100f, 40f).pad(2f).color(table.color);
}
@Override
public LInstruction build(LAssembler builder){
return new PrintI(builder.var(value));
}
@Override
public LCategory category(){
return LCategory.control;
}
}
public static class JumpStatement extends LStatement{ public static class JumpStatement extends LStatement{
public transient StatementElem dest; public transient StatementElem dest;
public int destIndex; public int destIndex;
public String condition = "true"; public String condition = "true";
@@ -131,20 +152,21 @@ public class LStatements{
//elements need separate conversion logic //elements need separate conversion logic
@Override @Override
public void afterLoad(LAssembler assembler){ public void setupUI(){
if(assembler.elements != null){ if(elem != null){
dest = assembler.elements.get(destIndex); dest = (StatementElem)elem.parent.getChildren().get(destIndex);
} }
} }
@Override @Override
public void beforeSave(LAssembler assembler){ public void saveUI(){
destIndex = dest == null ? -1 : dest.parent.getChildren().indexOf(dest); if(elem != null){
destIndex = dest == null ? -1 : dest.parent.getChildren().indexOf(dest);
}
} }
@Override @Override
public LInstruction build(LAssembler builder){ public LInstruction build(LAssembler builder){
beforeSave(builder);
return new JumpI(builder.var(condition),destIndex); return new JumpI(builder.var(condition),destIndex);
} }

View File

@@ -1,7 +1,9 @@
package mindustry.world.blocks.logic; package mindustry.world.blocks.logic;
import arc.util.io.*;
import mindustry.*; import mindustry.*;
import mindustry.gen.*; import mindustry.gen.*;
import mindustry.logic.*;
import mindustry.world.*; import mindustry.world.*;
public class LogicProcessor extends Block{ public class LogicProcessor extends Block{
@@ -10,19 +12,46 @@ public class LogicProcessor extends Block{
super(name); super(name);
update = true; update = true;
configurable = true; configurable = true;
config(String.class, (LogicEntity entity, String code) -> {
if(code != null){
entity.code = code;
entity.executor.load(entity, LAssembler.fromJson(code));
}
});
} }
public class LogicEntity extends Building{ public class LogicEntity extends Building{
/** logic "source code" as list of json statements */
String code = "[]";
LExecutor executor = new LExecutor();
@Override @Override
public void updateTile(){ public void updateTile(){
if(executor.initialized()){
executor.runAll();
}
} }
@Override @Override
public boolean configTapped(){ public boolean configTapped(){
Vars.ui.logic.show(); Vars.ui.logic.show(code, this::configure);
return false; return false;
} }
@Override
public void write(Writes write){
super.write(write);
write.str(code);
}
@Override
public void read(Reads read, byte revision){
super.read(read, revision);
code = read.str();
executor.load(this, LAssembler.fromJson(code));
}
} }
} }