From d6a294e14604aecd9046cbc19dc1837ca694fbb0 Mon Sep 17 00:00:00 2001 From: Elizabeth Hunt Date: Sun, 15 Feb 2026 10:08:30 -0800 Subject: feat: Adding ValueEmitter, LambdaEvaluatorSystem --- .../AbstractionEngineGridWorld.java | 11 ++- .../abstractionengine/app/actor/Abstraction.java | 18 ++--- .../abstractionengine/app/actor/LifeGridActor.java | 3 +- .../liz/abstractionengine/app/config/Settings.java | 3 + .../app/life/EntropyAudioSystem.java | 2 +- .../abstractionengine/app/screen/GameScreen.java | 2 +- .../liz/abstractionengine/app/screen/MainMenu.java | 5 +- .../app/screen/game/GameScreenWorld.java | 7 +- .../game/system/GridInterpolationSystem.java | 5 +- .../screen/game/system/GridWorldBridgeSystem.java | 18 +++-- .../app/screen/game/system/RenderSystem.java | 2 +- .../app/ui/editor/vim/context/KeyContext.java | 28 +++----- .../ui/editor/vim/handler/NormalModeHandler.java | 6 +- .../ui/editor/vim/navigation/TextNavigation.java | 6 +- .../abstractionengine/component/LambdaTerm.java | 17 ----- .../abstractionengine/entity/EntityFactory.java | 5 ++ .../grid/component/GridInputState.java | 15 ---- .../grid/component/LambdaTerm.java | 12 ++++ .../grid/component/Substitution.java | 15 ++++ .../grid/system/BaseGridIndexSystem.java | 3 +- .../system/GridCollisionPropagatationSystem.java | 3 +- .../grid/system/GridMovementSystem.java | 3 +- .../grid/system/GridPhysicsSystem.java | 3 +- .../grid/system/LambdaEvaluatorSystem.java | 82 ++++++++++++++++++++++ 24 files changed, 179 insertions(+), 95 deletions(-) delete mode 100644 core/src/main/java/coffee/liz/abstractionengine/component/LambdaTerm.java create mode 100644 core/src/main/java/coffee/liz/abstractionengine/grid/component/LambdaTerm.java create mode 100644 core/src/main/java/coffee/liz/abstractionengine/grid/component/Substitution.java create mode 100644 core/src/main/java/coffee/liz/abstractionengine/grid/system/LambdaEvaluatorSystem.java (limited to 'core/src/main/java/coffee/liz/abstractionengine') diff --git a/core/src/main/java/coffee/liz/abstractionengine/AbstractionEngineGridWorld.java b/core/src/main/java/coffee/liz/abstractionengine/AbstractionEngineGridWorld.java index 806d05e..b2ccb5e 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/AbstractionEngineGridWorld.java +++ b/core/src/main/java/coffee/liz/abstractionengine/AbstractionEngineGridWorld.java @@ -5,22 +5,21 @@ import coffee.liz.abstractionengine.grid.system.GridCollisionPropagatationSystem import coffee.liz.abstractionengine.grid.system.GridIndexSystem; import coffee.liz.abstractionengine.grid.system.GridMovementSystem; import coffee.liz.abstractionengine.grid.system.GridPhysicsSystem; +import coffee.liz.abstractionengine.grid.system.LambdaEvaluatorSystem; import coffee.liz.ecs.DAGWorld; import coffee.liz.ecs.math.Vec2; +import coffee.liz.lambda.ast.SourceCode; import lombok.extern.log4j.Log4j2; import java.time.Duration; -import java.util.Map; /** Grid world implementation for the abstraction engine. */ @Log4j2 public class AbstractionEngineGridWorld extends DAGWorld { - /** Initialize world with systems and player. */ - public AbstractionEngineGridWorld(final Vec2 gridDimensions) { - super(Map.of(GridMovementSystem.class, new GridMovementSystem(), GridPhysicsSystem.class, - new GridPhysicsSystem(), GridIndexSystem.class, new GridIndexSystem(gridDimensions), - GridCollisionPropagatationSystem.class, new GridCollisionPropagatationSystem())); + public AbstractionEngineGridWorld(final Vec2 gridDimensions, final SourceCode.Syntax preferredSyntax) { + super(new GridMovementSystem(), new GridPhysicsSystem(), new GridIndexSystem(gridDimensions), + new GridCollisionPropagatationSystem(), new LambdaEvaluatorSystem(preferredSyntax)); } /** diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/actor/Abstraction.java b/core/src/main/java/coffee/liz/abstractionengine/app/actor/Abstraction.java index 3ea4701..86a573c 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/actor/Abstraction.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/actor/Abstraction.java @@ -22,7 +22,7 @@ public class Abstraction extends Actor { @Getter private final Animation animation; - private final Entity abstractionEntity; + // private final Entity abstractionEntity; private boolean isActive; private float stateTime = 0f; @@ -33,24 +33,20 @@ public class Abstraction extends Actor { public Abstraction() { final Texture texture = new Texture(Gdx.files.internal("sprites/abstraction.png")); final TextureRegion[][] tmp = TextureRegion.split(texture, texture.getWidth() / ABSTRACTION_FRAMES.getX(), - texture.getHeight() / ABSTRACTION_FRAMES.getY()); - final TextureRegion[] anim = { - tmp[0][0], - tmp[1][0], - tmp[2][0], - tmp[3][0] - }; + texture.getHeight() / ABSTRACTION_FRAMES.getY()); + final TextureRegion[] anim = {tmp[0][0], tmp[1][0], tmp[2][0], tmp[3][0]}; this.animation = new Animation<>(0.25f, anim); addListener(new InputListener() { @Override - public void enter (final InputEvent event, final float x, final float y, final int pointer, @Null final Actor fromActor) { + public void enter(final InputEvent event, final float x, final float y, final int pointer, + @Null final Actor fromActor) { log.info("Entered mouse {}", event); } - @Override - public boolean touchDown (final InputEvent event, final float x, final float y, final int pointer, final int button) { + public boolean touchDown(final InputEvent event, final float x, final float y, final int pointer, + final int button) { log.info("TouchDown!!!"); return false; } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/actor/LifeGridActor.java b/core/src/main/java/coffee/liz/abstractionengine/app/actor/LifeGridActor.java index c4bb5e3..8f75d97 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/actor/LifeGridActor.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/actor/LifeGridActor.java @@ -9,6 +9,7 @@ import coffee.liz.ecs.math.Vec2; import coffee.liz.ecs.math.Vec2f; import coffee.liz.ecs.math.Vec2i; import coffee.liz.ecs.model.World; +import coffee.liz.ecs.model.Query; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.g2d.Batch; @@ -98,7 +99,7 @@ public class LifeGridActor extends Actor { } private void drawCells(final Vec2 cellSize) { - world.query(Set.of(GridPosition.class, CellState.class)).forEach(entity -> { + world.resolve(Query.allOf(GridPosition.class, CellState.class)).forEach(entity -> { final CellState state = entity.get(CellState.class); final Vec2 gridPos = entity.get(GridPosition.class).getPosition(); final Vec2 drawPos = gridPos.floatValue().scale(cellSize).plus(parallaxOffset); diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/config/Settings.java b/core/src/main/java/coffee/liz/abstractionengine/app/config/Settings.java index 7662700..b5ed2d1 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/config/Settings.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/config/Settings.java @@ -1,5 +1,6 @@ package coffee.liz.abstractionengine.app.config; +import coffee.liz.lambda.ast.SourceCode; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; @@ -14,6 +15,8 @@ public class Settings { private boolean playMusic = true; @Builder.Default private boolean vimMode = true; + @Builder.Default + private SourceCode.Syntax preferredSyntax = SourceCode.Syntax.ARROW; @Builder.Default private KeyBinds keyBinds = KeyBinds.builder().build(); diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/life/EntropyAudioSystem.java b/core/src/main/java/coffee/liz/abstractionengine/app/life/EntropyAudioSystem.java index 5660ff3..1ea5d32 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/life/EntropyAudioSystem.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/life/EntropyAudioSystem.java @@ -150,7 +150,7 @@ public class EntropyAudioSystem implements System { } @Override - public void close() { + public void dispose() { running = false; FunctionUtils.wrapCheckedException(() -> audioThread.join(AUDIO_THREAD_TIMEOUT)); device.dispose(); diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/screen/GameScreen.java b/core/src/main/java/coffee/liz/abstractionengine/app/screen/GameScreen.java index 70bb973..e96127c 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/screen/GameScreen.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/screen/GameScreen.java @@ -65,6 +65,6 @@ public class GameScreen implements Screen { @Override public void dispose() { - FunctionUtils.wrapCheckedException(outerWorld::close); + FunctionUtils.wrapCheckedException(outerWorld::dispose); } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/screen/MainMenu.java b/core/src/main/java/coffee/liz/abstractionengine/app/screen/MainMenu.java index 46b5192..b8cfbbc 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/screen/MainMenu.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/screen/MainMenu.java @@ -34,8 +34,7 @@ public class MainMenu implements Screen { private static final Vec2 BUTTON_SIZE = AbstractionEngineGame.WORLD_SIZE.scale(1 / 4f, 1 / 15f); private static final float PARALLAX_STRENGTH = 3f; - private final World world = new DAGWorld<>(Map.of(LifeSystem.class, new LifeSystem(GRID_DIMENSIONS), - EntropyAudioSystem.class, new EntropyAudioSystem())); + private final World world = new DAGWorld<>(new LifeSystem(GRID_DIMENSIONS), new EntropyAudioSystem()); private final AbstractionEngineGame game; private final Set> initialAlive = createGliderPattern(); @@ -162,7 +161,7 @@ public class MainMenu implements Screen { */ @Override public void dispose() { - FunctionUtils.wrapCheckedException(world::close); + FunctionUtils.wrapCheckedException(world::dispose); stage.dispose(); } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/GameScreenWorld.java b/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/GameScreenWorld.java index 137a5b0..260c191 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/GameScreenWorld.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/GameScreenWorld.java @@ -9,12 +9,9 @@ import coffee.liz.ecs.DAGWorld; import coffee.liz.ecs.math.Vec2; import com.badlogic.gdx.utils.viewport.FitViewport; -import java.util.Map; - public class GameScreenWorld extends DAGWorld { public GameScreenWorld(final Vec2 gridSize, final FitViewport viewport, final Settings settings) { - super(Map.of(InputSystem.class, new InputSystem(), GridWorldBridgeSystem.class, - new GridWorldBridgeSystem(gridSize), GridInterpolationSystem.class, new GridInterpolationSystem(), - RenderSystem.class, new RenderSystem(viewport, gridSize, settings))); + super(new InputSystem(), new GridWorldBridgeSystem(gridSize, settings), new GridInterpolationSystem(), + new RenderSystem(viewport, gridSize, settings)); } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/system/GridInterpolationSystem.java b/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/system/GridInterpolationSystem.java index 0d2d34b..d0acfbc 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/system/GridInterpolationSystem.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/system/GridInterpolationSystem.java @@ -10,6 +10,7 @@ import coffee.liz.ecs.math.Vec2f; import coffee.liz.ecs.model.Entity; import coffee.liz.ecs.model.System; import coffee.liz.ecs.model.World; +import coffee.liz.ecs.model.Query; import java.time.Duration; import java.util.Collection; @@ -33,7 +34,7 @@ public class GridInterpolationSystem implements System { public void update(final World world, final FrameState state, final Duration dt) { final GridWorldBridgeSystem bridgeSystem = world.getSystem(GridWorldBridgeSystem.class); - world.query(Set.of(ExternalWorldEntityRef.class, ActorHolder.class)).forEach(entity -> { + world.resolve(Query.allOf(ExternalWorldEntityRef.class, ActorHolder.class)).forEach(entity -> { final Actor actor = entity.get(ActorHolder.class).getActor(); final boolean isAlreadyInterpolating = !actor.getActions().isEmpty(); if (isAlreadyInterpolating) { @@ -62,7 +63,7 @@ public class GridInterpolationSystem implements System { } private Interpolation interpolationType(final EntityType type) { - return switch(type) { + return switch (type) { case PLAYER -> Interpolation.pow3; default -> new Interpolation.Swing(1.15f); }; diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/system/GridWorldBridgeSystem.java b/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/system/GridWorldBridgeSystem.java index dac5a32..3d76853 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/system/GridWorldBridgeSystem.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/system/GridWorldBridgeSystem.java @@ -5,6 +5,7 @@ import coffee.liz.abstractionengine.app.AbstractionEngineGame; import coffee.liz.abstractionengine.app.actor.Abstraction; import coffee.liz.abstractionengine.app.actor.Penguin; import coffee.liz.abstractionengine.app.actor.Wall; +import coffee.liz.abstractionengine.app.config.Settings; import coffee.liz.abstractionengine.app.screen.game.FrameState; import coffee.liz.abstractionengine.app.screen.game.component.ActorHolder; import coffee.liz.abstractionengine.app.screen.game.component.ExternalWorldEntityRef; @@ -18,6 +19,7 @@ import coffee.liz.ecs.math.Vec2i; import coffee.liz.ecs.model.Entity; import coffee.liz.ecs.model.System; import coffee.liz.ecs.model.World; +import coffee.liz.ecs.model.Query; import lombok.Getter; import java.time.Duration; @@ -41,12 +43,14 @@ public class GridWorldBridgeSystem implements System { @Getter private final Vec2 worldToGrid; - public GridWorldBridgeSystem(final Vec2 gridSize) { + public GridWorldBridgeSystem(final Vec2 gridSize, final Settings settings) { this.gridSize = gridSize; - this.outerWorld = new AbstractionEngineGridWorld(gridSize); + this.outerWorld = new AbstractionEngineGridWorld(gridSize, settings.getPreferredSyntax()); - this.gridToWorld = AbstractionEngineGame.WORLD_SIZE.scale(gridSize.floatValue().transform(x -> 1f / x, y -> 1f / y)); - this.worldToGrid = gridSize.floatValue().scale(AbstractionEngineGame.WORLD_SIZE.floatValue().transform(x -> 1f/x, y -> 1f/y)); + this.gridToWorld = AbstractionEngineGame.WORLD_SIZE + .scale(gridSize.floatValue().transform(x -> 1f / x, y -> 1f / y)); + this.worldToGrid = gridSize.floatValue() + .scale(AbstractionEngineGame.WORLD_SIZE.floatValue().transform(x -> 1f / x, y -> 1f / y)); } public void initLevel() { @@ -71,7 +75,7 @@ public class GridWorldBridgeSystem implements System { } private void syncEntities(final World world) { - final Set gridEntities = outerWorld.query(Set.of(GridPosition.class, EntityType.class)); + final Set gridEntities = outerWorld.resolve(Query.allOf(GridPosition.class, EntityType.class)); final Set seenGridIds = new HashSet<>(); for (final Entity gridEntity : gridEntities) { @@ -100,8 +104,8 @@ public class GridWorldBridgeSystem implements System { } @Override - public void close() { - FunctionUtils.wrapCheckedException(outerWorld::close); + public void dispose() { + FunctionUtils.wrapCheckedException(outerWorld::dispose); } private Actor createActor(final EntityType type, final Stage stage) { diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/system/RenderSystem.java b/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/system/RenderSystem.java index 401ed89..32b9ebd 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/system/RenderSystem.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/screen/game/system/RenderSystem.java @@ -70,7 +70,7 @@ public class RenderSystem implements System { } @Override - public void close() { + public void dispose() { uiStage.dispose(); stage.dispose(); shapeRenderer.dispose(); diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/context/KeyContext.java b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/context/KeyContext.java index abae5fb..285bbde 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/context/KeyContext.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/context/KeyContext.java @@ -19,6 +19,9 @@ import coffee.liz.abstractionengine.app.ui.editor.vim.state.PendingOperator; import coffee.liz.abstractionengine.app.ui.editor.vim.state.VisualLineState; import coffee.liz.abstractionengine.app.ui.editor.vim.state.VisualState; import com.badlogic.gdx.Input; +import lombok.Getter; +import lombok.Setter; + import java.util.OptionalInt; import java.util.Set; import java.util.function.Consumer; @@ -31,6 +34,7 @@ public class KeyContext { private final Register register; private final VimConfig config; + @Getter private String statusText; private String lastSearchQuery; private SearchDirection lastSearchDirection; @@ -39,6 +43,7 @@ public class KeyContext { private String searchIteratorText; private int suppressedTypedCommands; private boolean suppressNextRedoTyped; + @Setter private KeyEvent currentKeyEvent; public KeyContext(final EditorBuffer editorBuffer, final Consumer statusUpdater, @@ -73,18 +78,10 @@ public class KeyContext { return config; } - public void setCurrentKeyEvent(final KeyEvent currentKeyEvent) { - this.currentKeyEvent = currentKeyEvent; - } - public KeyEvent currentKeyEvent() { return currentKeyEvent; } - public String getStatusText() { - return statusText; - } - public void publishStatus(final String status) { if (!status.equals(statusText)) { statusText = status; @@ -361,7 +358,7 @@ public class KeyContext { public void deleteCharUnderCursor() { final String text = editorBuffer.getText(); final int cursor = editorBuffer.getCursorPosition(); - if (cursor >= text.length() || text.charAt(cursor) == '\n') { + if (cursor >= text.length() || text.charAt(cursor) == ' ') { return; } register.setText(String.valueOf(text.charAt(cursor))); @@ -432,7 +429,9 @@ public class KeyContext { return; } - final int insertAt = Math.min(cursor + 1, text.length()); + final boolean cursorOnEmptyLine = cursor < text.length() && text.charAt(cursor) == '\n' + && navigation.findLineStart(text, cursor) == cursor; + final int insertAt = cursorOnEmptyLine ? cursor : Math.min(cursor + 1, text.length()); editorBuffer.setText(text.substring(0, insertAt) + register.getText() + text.substring(insertAt)); editorBuffer.setCursorPosition(insertAt + register.getText().length() - 1); invalidateSearchIterator(); @@ -452,9 +451,8 @@ public class KeyContext { return; } - final int insertAt = cursor; - editorBuffer.setText(text.substring(0, insertAt) + register.getText() + text.substring(insertAt)); - editorBuffer.setCursorPosition(insertAt + register.getText().length() - 1); + editorBuffer.setText(text.substring(0, cursor) + register.getText() + text.substring(cursor)); + editorBuffer.setCursorPosition(cursor + register.getText().length() - 1); invalidateSearchIterator(); } @@ -737,10 +735,6 @@ public class KeyContext { editorBuffer.clearSelection(); } - public void setSelectionEndpoints(final int selectionStart, final int cursorPosition) { - editorBuffer.setSelectionEndpoints(selectionStart, cursorPosition); - } - public Register register() { return register; } diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/handler/NormalModeHandler.java b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/handler/NormalModeHandler.java index bd9b1d0..95dc2bc 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/handler/NormalModeHandler.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/handler/NormalModeHandler.java @@ -84,8 +84,8 @@ public class NormalModeHandler implements ModeHandler { if (character == 'i' && (operator == PendingOperator.DELETE || operator == PendingOperator.YANK || operator == PendingOperator.CHANGE)) { - return HandleResult.handled(new NormalState(state.count(), mapInnerOperator(operator), - state.preferredColumn())); + return HandleResult + .handled(new NormalState(state.count(), mapInnerOperator(operator), state.preferredColumn())); } if (isInnerWordOperator(operator)) { @@ -96,7 +96,7 @@ public class NormalModeHandler implements ModeHandler { } final int start = context.cursor(); - final int updatedPreferred = applyMotion(character, repeats, state.preferredColumn(), context, true); + final int updatedPreferred = applyMotion(character, repeats, state.preferredColumn(), context, false); int end = context.cursor(); if (character == '$') { end = Math.min(context.text().length(), end + 1); diff --git a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/navigation/TextNavigation.java b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/navigation/TextNavigation.java index b74c1df..10af095 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/navigation/TextNavigation.java +++ b/core/src/main/java/coffee/liz/abstractionengine/app/ui/editor/vim/navigation/TextNavigation.java @@ -85,7 +85,11 @@ public class TextNavigation { return 0; } - return text.length() - 1; + int index = text.length() - 1; + while (index > 0 && (text.charAt(index) == '\n' || text.charAt(index) == '\r')) { + index--; + } + return index; } public int maxCursorPositionInNormalMode(final String text) { diff --git a/core/src/main/java/coffee/liz/abstractionengine/component/LambdaTerm.java b/core/src/main/java/coffee/liz/abstractionengine/component/LambdaTerm.java deleted file mode 100644 index b0d1358..0000000 --- a/core/src/main/java/coffee/liz/abstractionengine/component/LambdaTerm.java +++ /dev/null @@ -1,17 +0,0 @@ -package coffee.liz.abstractionengine.component; - -import coffee.liz.ecs.math.Vec2; -import coffee.liz.ecs.model.Component; -import coffee.liz.lambda.ast.Expression; -import lombok.Data; -import lombok.RequiredArgsConstructor; - -import java.util.Map; - -@RequiredArgsConstructor -@Data -public class LambdaTerm implements Component { - private final Map> termDependencies; - - private final Expression term; -} diff --git a/core/src/main/java/coffee/liz/abstractionengine/entity/EntityFactory.java b/core/src/main/java/coffee/liz/abstractionengine/entity/EntityFactory.java index d8299aa..871c6d0 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/entity/EntityFactory.java +++ b/core/src/main/java/coffee/liz/abstractionengine/entity/EntityFactory.java @@ -2,6 +2,7 @@ package coffee.liz.abstractionengine.entity; import coffee.liz.abstractionengine.grid.component.GridControllable; import coffee.liz.abstractionengine.grid.component.GridPosition; +import coffee.liz.abstractionengine.grid.component.LambdaTerm; import coffee.liz.ecs.math.Mat2; import coffee.liz.ecs.math.Vec2; import coffee.liz.ecs.model.Entity; @@ -51,6 +52,10 @@ public final class EntityFactory { return world.createEntity().add(new GridPosition(position)).add(EntityType.ABSTRACTION) .add(new EntityTypeGridCollidable()); } + + public static Entity addToWorld(final World world, final Vec2 position, final LambdaTerm term) { + return addToWorld(world, position).add(term); + } } /** diff --git a/core/src/main/java/coffee/liz/abstractionengine/grid/component/GridInputState.java b/core/src/main/java/coffee/liz/abstractionengine/grid/component/GridInputState.java index 6ab0d2b..5134719 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/grid/component/GridInputState.java +++ b/core/src/main/java/coffee/liz/abstractionengine/grid/component/GridInputState.java @@ -19,19 +19,4 @@ public class GridInputState { /** Movement {@link Vec2} direction. */ @Nullable private final Vec2 movement; - - /** Lambda term update to apply. */ - @Nullable - private final UpdateLambdaTerm updateLambdaTerm; - - /** Update lambda term with target entity. */ - @Getter - @RequiredArgsConstructor - public static class UpdateLambdaTerm { - /** {@link Entity} to update. */ - private final Entity toUpdate; - - /** {@link LambdaProgram} to apply. */ - private final LambdaProgram lambdaProgram; - } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/grid/component/LambdaTerm.java b/core/src/main/java/coffee/liz/abstractionengine/grid/component/LambdaTerm.java new file mode 100644 index 0000000..9ed1e9c --- /dev/null +++ b/core/src/main/java/coffee/liz/abstractionengine/grid/component/LambdaTerm.java @@ -0,0 +1,12 @@ +package coffee.liz.abstractionengine.grid.component; + +import coffee.liz.ecs.model.Component; +import coffee.liz.lambda.ast.SourceCode; +import lombok.Data; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Data +public class LambdaTerm implements Component { + private final SourceCode term; +} diff --git a/core/src/main/java/coffee/liz/abstractionengine/grid/component/Substitution.java b/core/src/main/java/coffee/liz/abstractionengine/grid/component/Substitution.java new file mode 100644 index 0000000..77ff4e8 --- /dev/null +++ b/core/src/main/java/coffee/liz/abstractionengine/grid/component/Substitution.java @@ -0,0 +1,15 @@ +package coffee.liz.abstractionengine.grid.component; + +import coffee.liz.ecs.math.Vec2; +import coffee.liz.ecs.model.Component; +import lombok.Data; +import lombok.RequiredArgsConstructor; + +import java.util.Map; + +@RequiredArgsConstructor +@Data +public class Substitution implements Component { + private final Vec2 spawnMomentum; + private final Map> termDependencies; +} diff --git a/core/src/main/java/coffee/liz/abstractionengine/grid/system/BaseGridIndexSystem.java b/core/src/main/java/coffee/liz/abstractionengine/grid/system/BaseGridIndexSystem.java index ee77256..470c72f 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/grid/system/BaseGridIndexSystem.java +++ b/core/src/main/java/coffee/liz/abstractionengine/grid/system/BaseGridIndexSystem.java @@ -4,6 +4,7 @@ import coffee.liz.abstractionengine.grid.component.GridPosition; import coffee.liz.ecs.math.Mat2; import coffee.liz.ecs.math.Vec2; import coffee.liz.ecs.model.Entity; +import coffee.liz.ecs.model.Query; import coffee.liz.ecs.model.System; import coffee.liz.ecs.model.World; import lombok.Getter; @@ -36,7 +37,7 @@ public abstract class BaseGridIndexSystem implements System { @Override public void update(final World world, final T state, final Duration dt) { rows.forEach(row -> row.forEach(Set::clear)); - world.query(Set.of(GridPosition.class)).forEach(entity -> { + world.resolve(Query.allOf(GridPosition.class)).forEach(entity -> { final GridPosition position = entity.get(GridPosition.class); rows.get(position.getPosition().getY()).get(position.getPosition().getX()).add(entity); }); diff --git a/core/src/main/java/coffee/liz/abstractionengine/grid/system/GridCollisionPropagatationSystem.java b/core/src/main/java/coffee/liz/abstractionengine/grid/system/GridCollisionPropagatationSystem.java index 23a821d..9980795 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/grid/system/GridCollisionPropagatationSystem.java +++ b/core/src/main/java/coffee/liz/abstractionengine/grid/system/GridCollisionPropagatationSystem.java @@ -10,6 +10,7 @@ import coffee.liz.ecs.math.Vec2i; import coffee.liz.ecs.model.Entity; import coffee.liz.ecs.model.System; import coffee.liz.ecs.model.World; +import coffee.liz.ecs.model.Query; import java.time.Duration; import java.util.ArrayList; @@ -31,7 +32,7 @@ public class GridCollisionPropagatationSystem implements System @Override public void update(final World world, final GridInputState state, final Duration dt) { - world.query(Set.of(GridMomentum.class, GridPosition.class, GridCollidable.class)).forEach(pusher -> { + world.resolve(Query.allOf(GridMomentum.class, GridPosition.class, GridCollidable.class)).forEach(pusher -> { final Vec2 velocity = pusher.get(GridMomentum.class).getVelocity(); if (velocity.equals(Vec2i.ZERO)) { return; diff --git a/core/src/main/java/coffee/liz/abstractionengine/grid/system/GridMovementSystem.java b/core/src/main/java/coffee/liz/abstractionengine/grid/system/GridMovementSystem.java index 02f012b..c402fcc 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/grid/system/GridMovementSystem.java +++ b/core/src/main/java/coffee/liz/abstractionengine/grid/system/GridMovementSystem.java @@ -6,6 +6,7 @@ import coffee.liz.abstractionengine.grid.component.GridMomentum; import coffee.liz.abstractionengine.grid.component.GridPosition; import coffee.liz.ecs.model.System; import coffee.liz.ecs.model.World; +import coffee.liz.ecs.model.Query; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -25,7 +26,7 @@ public class GridMovementSystem implements System { @Override public void update(final World world, final GridInputState state, final Duration dt) { - world.query(Set.of(GridControllable.class, GridPosition.class)) + world.resolve(Query.allOf(GridControllable.class, GridPosition.class)) .forEach(entity -> entity.add(new GridMomentum(state.getMovement()))); } } diff --git a/core/src/main/java/coffee/liz/abstractionengine/grid/system/GridPhysicsSystem.java b/core/src/main/java/coffee/liz/abstractionengine/grid/system/GridPhysicsSystem.java index 5e0e632..676dd1b 100644 --- a/core/src/main/java/coffee/liz/abstractionengine/grid/system/GridPhysicsSystem.java +++ b/core/src/main/java/coffee/liz/abstractionengine/grid/system/GridPhysicsSystem.java @@ -6,6 +6,7 @@ import coffee.liz.abstractionengine.grid.component.GridPosition; import coffee.liz.ecs.math.Vec2i; import coffee.liz.ecs.model.System; import coffee.liz.ecs.model.World; +import coffee.liz.ecs.model.Query; import java.time.Duration; import java.util.Collection; @@ -20,7 +21,7 @@ public class GridPhysicsSystem implements System { @Override public void update(final World world, final GridInputState state, final Duration dt) { - world.query(Set.of(GridMomentum.class, GridPosition.class)).forEach(entity -> { + world.resolve(Query.allOf(GridMomentum.class, GridPosition.class)).forEach(entity -> { final GridMomentum momentum = entity.get(GridMomentum.class); final GridPosition position = entity.get(GridPosition.class); diff --git a/core/src/main/java/coffee/liz/abstractionengine/grid/system/LambdaEvaluatorSystem.java b/core/src/main/java/coffee/liz/abstractionengine/grid/system/LambdaEvaluatorSystem.java new file mode 100644 index 0000000..74bc3fc --- /dev/null +++ b/core/src/main/java/coffee/liz/abstractionengine/grid/system/LambdaEvaluatorSystem.java @@ -0,0 +1,82 @@ +package coffee.liz.abstractionengine.grid.system; + +import coffee.liz.abstractionengine.entity.EntityFactory; +import coffee.liz.abstractionengine.entity.EntityType; +import coffee.liz.abstractionengine.grid.component.GridInputState; +import coffee.liz.abstractionengine.grid.component.GridMomentum; +import coffee.liz.abstractionengine.grid.component.GridPosition; +import coffee.liz.abstractionengine.grid.component.LambdaTerm; +import coffee.liz.abstractionengine.grid.component.Substitution; +import coffee.liz.ecs.math.Vec2; +import coffee.liz.ecs.model.System; +import coffee.liz.ecs.model.World; +import coffee.liz.ecs.model.Query; +import coffee.liz.lambda.LambdaDriver; +import coffee.liz.lambda.ast.SourceCode; +import coffee.liz.lambda.eval.Environment; +import coffee.liz.lambda.eval.Value; +import coffee.liz.lambda.format.ValueEmitter; +import lombok.extern.log4j.Log4j2; + +import java.time.Duration; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +@Log4j2 +public class LambdaEvaluatorSystem implements System { + private final SourceCode.Syntax preferredSyntax; + + public LambdaEvaluatorSystem(final SourceCode.Syntax preferredSyntax) { + this.preferredSyntax = preferredSyntax; + } + + @Override + public Collection>> getDependencies() { + return List.of(GridCollisionPropagatationSystem.class); + } + + @Override + public void update(final World world, final GridInputState state, final Duration dt) { + final GridIndexSystem gridIndexSystem = world.getSystem(GridIndexSystem.class); + + world.resolve(Query.allOf(Substitution.class, LambdaTerm.class, GridPosition.class)).forEach(entity -> { + final LambdaTerm term = entity.get(LambdaTerm.class); + final Substitution substitution = entity.get(Substitution.class); + final GridPosition position = entity.get(GridPosition.class); + + final Map> dependencies = substitution.getTermDependencies().entrySet().stream() + .map(e -> { + final String symbol = e.getKey(); + // Ideally we should only ever have one match to replace the symbol with. + // But maybe there's a use-case where this is not the case? + final Optional sourceCode = gridIndexSystem.entitiesAt(e.getValue()).stream() + .filter(_entity -> _entity.has(LambdaTerm.class)) + .map(_entity -> _entity.get(LambdaTerm.class)).map(LambdaTerm::getTerm).findFirst(); + // TODO: Map error + final Optional> val = sourceCode.map(s -> () -> LambdaDriver.interpret(s)); + return Map.entry(symbol, val); + }).filter(e -> e.getValue().isPresent()) + .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().get())); + + final boolean dependenciesSatisified = dependencies.keySet() + .equals(substitution.getTermDependencies().keySet()); + if (!dependenciesSatisified) { + return; + } + + final Environment env = dependencies.entrySet().stream().reduce(Environment.root(), + (_env, e) -> _env.extend(e.getKey(), e.getValue()), (a, b) -> b); + final Value evaluationResult = LambdaDriver.interpret(term.getTerm(), List.of(), env); + final SourceCode emittedResult = ValueEmitter.emit(evaluationResult, preferredSyntax); + final Vec2 spawnMomentum = substitution.getSpawnMomentum(); + + EntityFactory.Abstraction.addToWorld(world, position.getPosition(), new LambdaTerm(emittedResult)) + .add(EntityType.ABSTRACTION).add(new GridPosition(position.getPosition())) + .add(new GridMomentum(spawnMomentum)); + }); + } +} -- cgit v1.3