From 07b93f1420812c24e723dd0d46ad1bbdbd7754cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20B=C3=BChler?= <17296905+buehlefs@users.noreply.github.com> Date: Wed, 15 Jul 2020 17:20:37 +0200 Subject: [PATCH 1/4] Add task verifier display to ui and all related interfaces --- .../manualstart/ManualStartSimulation.java | 1 + .../icge/manualstart/TestTaskVerifier.java | 87 +++++++++ .../fius/icge/simulation/Simulation.java | 7 + .../icge/simulation/SimulationBuilder.java | 8 +- .../fius/icge/simulation/TaskVerifier.java | 46 +++++ .../internal/StandardSimulation.java | 16 +- .../internal/StandardSimulationProxy.java | 25 ++- .../informatik/fius/icge/ui/GameWindow.java | 9 +- .../fius/icge/ui/SimulationProxy.java | 11 +- .../fius/icge/ui/TaskInformation.java | 50 +++++ .../fius/icge/ui/TaskStatusDisplay.java | 27 +++ .../fius/icge/ui/TaskVerificationStatus.java | 22 +++ .../fius/icge/ui/WindowBuilder.java | 4 +- .../internal/ConsoleBufferedOutputStream.java | 8 +- .../icge/ui/internal/SwingGameWindow.java | 26 ++- .../ui/internal/SwingTaskStatusDisplay.java | 180 ++++++++++++++++++ .../fius/icge/ui/SwingGameWindowUiTest.java | 4 +- 17 files changed, 509 insertions(+), 22 deletions(-) create mode 100644 ICGE-ManualStart/src/main/java/de/unistuttgart/informatik/fius/icge/manualstart/TestTaskVerifier.java create mode 100644 ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/TaskVerifier.java create mode 100644 ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/TaskInformation.java create mode 100644 ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/TaskStatusDisplay.java create mode 100644 ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/TaskVerificationStatus.java create mode 100644 ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/internal/SwingTaskStatusDisplay.java diff --git a/ICGE-ManualStart/src/main/java/de/unistuttgart/informatik/fius/icge/manualstart/ManualStartSimulation.java b/ICGE-ManualStart/src/main/java/de/unistuttgart/informatik/fius/icge/manualstart/ManualStartSimulation.java index ffd257810..b80867796 100644 --- a/ICGE-ManualStart/src/main/java/de/unistuttgart/informatik/fius/icge/manualstart/ManualStartSimulation.java +++ b/ICGE-ManualStart/src/main/java/de/unistuttgart/informatik/fius/icge/manualstart/ManualStartSimulation.java @@ -42,6 +42,7 @@ public static void main(final String[] args) { TestEntity.TEXTURE_HANDLE = ManualStartSimulation.animated; final SimulationBuilder sb = new SimulationBuilder(); + sb.setTaskVerifier(new TestTaskVerifier()); sb.buildSimulation(); final Simulation sim = sb.getBuiltSimulation(); diff --git a/ICGE-ManualStart/src/main/java/de/unistuttgart/informatik/fius/icge/manualstart/TestTaskVerifier.java b/ICGE-ManualStart/src/main/java/de/unistuttgart/informatik/fius/icge/manualstart/TestTaskVerifier.java new file mode 100644 index 000000000..117c8420c --- /dev/null +++ b/ICGE-ManualStart/src/main/java/de/unistuttgart/informatik/fius/icge/manualstart/TestTaskVerifier.java @@ -0,0 +1,87 @@ +/* + * This source file is part of the FIUS ICGE project. + * For more information see github.com/FIUS/ICGE2 + * + * Copyright (c) 2019 the ICGE project authors. + * + * This software is available under the MIT license. + * SPDX-License-Identifier: MIT + */ +package de.unistuttgart.informatik.fius.icge.manualstart; + +import java.util.List; + +import de.unistuttgart.informatik.fius.icge.simulation.Simulation; +import de.unistuttgart.informatik.fius.icge.simulation.TaskVerifier; +import de.unistuttgart.informatik.fius.icge.simulation.actions.ActionLog; +import de.unistuttgart.informatik.fius.icge.simulation.actions.EntityMoveAction; +import de.unistuttgart.informatik.fius.icge.ui.TaskInformation; +import de.unistuttgart.informatik.fius.icge.ui.TaskVerificationStatus; + + +/** + * Example task verifier. + * + * @author Fabian Bühler + */ +public class TestTaskVerifier implements TaskVerifier, TaskInformation { + + private ActionLog log; + private TaskVerificationStatus taskIsValid = TaskVerificationStatus.UNDECIDED; + private int stepsToWalk = 4; + + @Override + public void attachToSimulation(Simulation sim) { + // get the log to verify if the required events did happen later + this.log = sim.getActionLog(); + } + + @Override + public void verify() { + if (this.log == null) { + return; + } + // check the number of steps/moves all entity have taken together + int steps = this.log.getActionsOfType(EntityMoveAction.class, true).size(); + if (steps > 3) { + this.stepsToWalk = 0; + this.taskIsValid = TaskVerificationStatus.SUCCESSFUL; + } else { + this.stepsToWalk = 4 - steps; + this.taskIsValid = TaskVerificationStatus.FAILED; + } + } + + @Override + public TaskInformation getTaskInformation() { + return this; + } + + @Override + public String getTaskTitle() { + return "Test Task"; + } + + @Override + public String getTaskDescription() { + String description = "Just a demo task to test the UI and the Backend."; + if (this.stepsToWalk > 1) { + description += " (" + this.stepsToWalk + " steps to walk)"; + } + if (this.stepsToWalk == 1) { + description += " (1 step to walk)"; + } + return description; + } + + @Override + public TaskVerificationStatus getTaskStatus() { + return this.taskIsValid; + } + + @Override + public List getChildTasks() { + return null; + } + +} diff --git a/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/Simulation.java b/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/Simulation.java index 34610c1ea..f8025a20e 100644 --- a/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/Simulation.java +++ b/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/Simulation.java @@ -31,6 +31,13 @@ public interface Simulation { */ Playfield getPlayfield(); + /** + * Get the task verifier set for this simulation. + * + * @return The task verifier set for this simulation + */ + TaskVerifier getTaskVerifier(); + /** * Get the simulation clock for this simulation. * diff --git a/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/SimulationBuilder.java b/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/SimulationBuilder.java index 5edbf0de1..4c4ebbecd 100644 --- a/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/SimulationBuilder.java +++ b/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/SimulationBuilder.java @@ -26,6 +26,8 @@ */ public class SimulationBuilder { + private TaskVerifier taskVerifier; + private Simulation simulation; /** @@ -39,12 +41,13 @@ public class SimulationBuilder { * @param taskVerifier * The task verifier to use */ - public void setTaskVerifier(final Object taskVerifier) { + public void setTaskVerifier(final TaskVerifier taskVerifier) { if ( this.hasBuiltSimulation() ) throw new IllegalStateException( "The simulation was already built! Use the methods of the Simulation Object to change its properties." ); + this.taskVerifier = taskVerifier; } /** @@ -71,7 +74,8 @@ public void buildSimulation() { final InspectionManager inspectionManager = new InspectionManager(); this.simulation = new StandardSimulation( - playfield, simulationClock, entityTypeRegistry, entityProgramRegistry, entityProgramRunner, actionLog, inspectionManager + playfield, simulationClock, entityTypeRegistry, entityProgramRegistry, entityProgramRunner, actionLog, inspectionManager, + this.taskVerifier ); } diff --git a/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/TaskVerifier.java b/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/TaskVerifier.java new file mode 100644 index 000000000..da571ad7d --- /dev/null +++ b/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/TaskVerifier.java @@ -0,0 +1,46 @@ +/* + * This source file is part of the FIUS ICGE project. + * For more information see github.com/FIUS/ICGE2 + * + * Copyright (c) 2019 the ICGE project authors. + * + * This software is available under the MIT license. + * SPDX-License-Identifier: MIT + */ +package de.unistuttgart.informatik.fius.icge.simulation; + +import de.unistuttgart.informatik.fius.icge.ui.TaskInformation; + + +/** + * The interface for a verifier of a task. + * + * @author Fabian Bühler + */ +public interface TaskVerifier { + /** + * Attach the verifier to a simulation. + *

+ * Must be called by the simulation or the simulation builder. + * + * @param sim + * the simulation to verify + */ + void attachToSimulation(Simulation sim); + + /** + * Verify if the current state of the simulation matches the requirements for the successful completion of the task. + *

+ * This method should update the task information returned by {@link #getTaskInformation}. + */ + void verify(); + + /** + * Get the current task information. + *

+ * The information returned by this method should not change unless {@link #verify} is called. + * + * @return the task information + */ + TaskInformation getTaskInformation(); +} diff --git a/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/internal/StandardSimulation.java b/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/internal/StandardSimulation.java index 8e3d8f3b6..81db00559 100644 --- a/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/internal/StandardSimulation.java +++ b/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/internal/StandardSimulation.java @@ -12,6 +12,7 @@ import de.unistuttgart.informatik.fius.icge.simulation.Playfield; import de.unistuttgart.informatik.fius.icge.simulation.Simulation; import de.unistuttgart.informatik.fius.icge.simulation.SimulationClock; +import de.unistuttgart.informatik.fius.icge.simulation.TaskVerifier; import de.unistuttgart.informatik.fius.icge.simulation.actions.ActionLog; import de.unistuttgart.informatik.fius.icge.simulation.entity.EntityTypeRegistry; import de.unistuttgart.informatik.fius.icge.simulation.entity.program.EntityProgramRegistry; @@ -41,6 +42,7 @@ public class StandardSimulation implements Simulation { private final StandardEntityProgramRunner entityProgramRunner; private final StandardActionLog actionLog; private final StandardEntityTypeRegistry entityTypeRegistry; + private final TaskVerifier taskVerifier; private final StandardSimulationProxy simulationProxy; /** @@ -60,12 +62,14 @@ public class StandardSimulation implements Simulation { * The actionLog to use * @param inspectionManager * The inspection manager to use + * @param taskVerifier + * the task verifier to use to verify the task completion status */ public StandardSimulation( final StandardPlayfield playfield, final StandardSimulationClock simulationClock, final StandardEntityTypeRegistry entityTypeRegistry, final StandardEntityProgramRegistry entityProgramRegistry, final StandardEntityProgramRunner entityProgramRunner, final StandardActionLog actionLog, - final InspectionManager inspectionManager + final InspectionManager inspectionManager, final TaskVerifier taskVerifier ) { this.playfield = playfield; this.simulationClock = simulationClock; @@ -73,11 +77,14 @@ public StandardSimulation( this.entityProgramRunner = entityProgramRunner; this.actionLog = actionLog; this.entityTypeRegistry = entityTypeRegistry; + this.taskVerifier = taskVerifier; this.playfield.initialize(this); + taskVerifier.attachToSimulation(this); + this.simulationProxy = new StandardSimulationProxy( - simulationClock, inspectionManager, entityTypeRegistry, playfield, entityProgramRegistry + simulationClock, inspectionManager, entityTypeRegistry, playfield, entityProgramRegistry, taskVerifier ); } @@ -86,6 +93,11 @@ public Playfield getPlayfield() { return this.playfield; } + @Override + public TaskVerifier getTaskVerifier() { + return this.taskVerifier; + } + @Override public SimulationClock getSimulationClock() { return this.simulationClock; diff --git a/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/internal/StandardSimulationProxy.java b/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/internal/StandardSimulationProxy.java index 37b559c49..dffd81f0b 100644 --- a/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/internal/StandardSimulationProxy.java +++ b/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/internal/StandardSimulationProxy.java @@ -18,6 +18,7 @@ import java.util.function.Consumer; import de.unistuttgart.informatik.fius.icge.simulation.Position; +import de.unistuttgart.informatik.fius.icge.simulation.TaskVerifier; import de.unistuttgart.informatik.fius.icge.simulation.entity.Entity; import de.unistuttgart.informatik.fius.icge.simulation.exception.CannotRunProgramException; import de.unistuttgart.informatik.fius.icge.simulation.exception.EntityNotOnFieldException; @@ -31,6 +32,7 @@ import de.unistuttgart.informatik.fius.icge.ui.GameWindow; import de.unistuttgart.informatik.fius.icge.ui.SimulationProxy; import de.unistuttgart.informatik.fius.icge.ui.SimulationTreeNode; +import de.unistuttgart.informatik.fius.icge.ui.TaskInformation; import de.unistuttgart.informatik.fius.icge.ui.Toolbar.ClockButtonState; import de.unistuttgart.informatik.fius.icge.ui.Toolbar.ControlButtonState; @@ -63,6 +65,7 @@ public class StandardSimulationProxy implements SimulationProxy { private final StandardSimulationClock simulationClock; private final StandardPlayfield playfield; private final StandardEntityProgramRegistry entityProgramRegistry; + private final TaskVerifier taskVerifier; private final Map simualtionSidebarMap; private Entity entityToInspect; @@ -80,17 +83,20 @@ public class StandardSimulationProxy implements SimulationProxy { * The playfield to use * @param entityProgramRegistry * the entity program registry + * @param taskVerifier + * the task verifier to use to verify the task completion status */ public StandardSimulationProxy( final StandardSimulationClock simulationClock, final InspectionManager inspectionManager, final StandardEntityTypeRegistry entityTypeRegistry, final StandardPlayfield playfield, - final StandardEntityProgramRegistry entityProgramRegistry + final StandardEntityProgramRegistry entityProgramRegistry, final TaskVerifier taskVerifier ) { this.simulationClock = simulationClock; this.inspectionManager = inspectionManager; this.entityTypeRegistry = entityTypeRegistry; this.playfield = playfield; this.entityProgramRegistry = entityProgramRegistry; + this.taskVerifier = taskVerifier; this.simualtionSidebarMap = new ConcurrentHashMap<>(); } @@ -171,6 +177,13 @@ public void accept(final List drawables) { return true; }); + // taskState + TaskInformation task = null; + if (this.taskVerifier != null) { + task = this.taskVerifier.getTaskInformation(); + } + this.gameWindow.getTaskStatusDisplay().setTaskInformation(task); + this.gameWindow.setSimulationProxy(this); } @@ -234,6 +247,16 @@ public void selectedEntityChanged(final String name) { this.gameWindow.getPlayfieldDrawer().setSelectedEntityType(name, textureHandle); } + @Override + public void refreshTaskInformation() { + this.taskVerifier.verify(); + TaskInformation task = null; + if (this.taskVerifier != null) { + task = this.taskVerifier.getTaskInformation(); + } + this.gameWindow.getTaskStatusDisplay().setTaskInformation(task); + } + @Override public Set getAvailableProgramsForEntityType(final String typeName) { try { diff --git a/ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/GameWindow.java b/ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/GameWindow.java index a3578166a..7458edda5 100644 --- a/ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/GameWindow.java +++ b/ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/GameWindow.java @@ -46,10 +46,17 @@ public interface GameWindow { /** * Get the console for this game window * - * @return The console used by thsi window + * @return The console used by this window */ Console getConsole(); + /** + * Get the task status display for this game window. + * + * @return The task status display used by this window + */ + TaskStatusDisplay getTaskStatusDisplay(); + /** * Set the title of the window, in which the ICGE is displayed. * diff --git a/ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/SimulationProxy.java b/ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/SimulationProxy.java index ceea0f444..1998424ad 100644 --- a/ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/SimulationProxy.java +++ b/ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/SimulationProxy.java @@ -14,10 +14,10 @@ /** * The SimulationProxy interface. This is used for communication most between the UI and the simulation. - * + *

* First the {@link #attachToGameWindow(GameWindow)} needs to be called to establish the connection. This should set up * all communication channels from the Simulation to the UI. - * + *

* The other methods are communication channels from the UI the Simulation. * * @author Tobias Wältken, Tim Neumann @@ -79,6 +79,13 @@ public enum ButtonType { */ void selectedEntityChanged(String name); + /** + * This gets called by the user to refresh task status information. + *

+ * Calling this must verify the task. + */ + void refreshTaskInformation(); + // // Entity placing // diff --git a/ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/TaskInformation.java b/ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/TaskInformation.java new file mode 100644 index 000000000..58cc3fa9c --- /dev/null +++ b/ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/TaskInformation.java @@ -0,0 +1,50 @@ +/* + * This source file is part of the FIUS ICGE project. + * For more information see github.com/FIUS/ICGE2 + * + * Copyright (c) 2019 the ICGE project authors. + * + * This software is available under the MIT license. + * SPDX-License-Identifier: MIT + */ +package de.unistuttgart.informatik.fius.icge.ui; + +import java.util.List; + + +/** + * A interface providing the title, description and status of a task. + * + * @author Fabian Bühler + * @version 1.0 + */ +public interface TaskInformation { + + /** + * Get the title of the task. + * + * @return the task title (must not be {@code null}, without trailing newline) + */ + String getTaskTitle(); + + /** + * Get the description of the task. + * + * @return the task description (can be {@code null}, can contain newlines, without trailing newline) + */ + String getTaskDescription(); + + /** + * Get the verification status of the task. + * + * @return the task status (must not be {@code null}) + */ + TaskVerificationStatus getTaskStatus(); + + /** + * Get a list of child/sub tasks of this task. + * + * @return a list of sub-tasks (may be {@code null} or empty) + */ + List getChildTasks(); +} diff --git a/ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/TaskStatusDisplay.java b/ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/TaskStatusDisplay.java new file mode 100644 index 000000000..a89de65cb --- /dev/null +++ b/ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/TaskStatusDisplay.java @@ -0,0 +1,27 @@ +/* + * This source file is part of the FIUS ICGE project. + * For more information see github.com/FIUS/ICGE2 + * + * Copyright (c) 2019 the ICGE project authors. + * + * This software is available under the MIT license. + * SPDX-License-Identifier: MIT + */ +package de.unistuttgart.informatik.fius.icge.ui; + +/** + * The task status used by a {@link GameWindow} to show the current task status. + * + * @author Fabian Bühler + * @version 1.0 + */ +public interface TaskStatusDisplay { + + /** + * Set the task information to be displayed. + * + * @param task + * the information of the current task (and subtasks) + */ + void setTaskInformation(TaskInformation task); +} diff --git a/ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/TaskVerificationStatus.java b/ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/TaskVerificationStatus.java new file mode 100644 index 000000000..584c9cfda --- /dev/null +++ b/ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/TaskVerificationStatus.java @@ -0,0 +1,22 @@ +/* + * This source file is part of the FIUS ICGE project. + * For more information see github.com/FIUS/ICGE2 + * + * Copyright (c) 2019 the ICGE project authors. + * + * This software is available under the MIT license. + * SPDX-License-Identifier: MIT + */ +package de.unistuttgart.informatik.fius.icge.ui; + +/** + * Enum describing the verification status of a task. + */ +public enum TaskVerificationStatus { + /** Undecided is for tasks that have not been verified (or have not failed yet). */ + UNDECIDED, + /** Successful is for tasks that have ben solved correctly. */ + SUCCESSFUL, + /** Failed is for tasks that have not been solved correctly. */ + FAILED, +} diff --git a/ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/WindowBuilder.java b/ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/WindowBuilder.java index f2be8042c..28bbaf077 100644 --- a/ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/WindowBuilder.java +++ b/ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/WindowBuilder.java @@ -16,6 +16,7 @@ import de.unistuttgart.informatik.fius.icge.ui.internal.SwingEntitySidebar; import de.unistuttgart.informatik.fius.icge.ui.internal.SwingGameWindow; import de.unistuttgart.informatik.fius.icge.ui.internal.SwingPlayfieldDrawer; +import de.unistuttgart.informatik.fius.icge.ui.internal.SwingTaskStatusDisplay; import de.unistuttgart.informatik.fius.icge.ui.internal.SwingTextureRegistry; import de.unistuttgart.informatik.fius.icge.ui.internal.SwingToolbar; @@ -99,11 +100,12 @@ public void buildWindow() { final SwingToolbar toolbar = new SwingToolbar(textureRegistry); final SwingEntitySidebar entitySidebar = new SwingEntitySidebar(textureRegistry); final SwingConsole console = new SwingConsole(); + final SwingTaskStatusDisplay taskStatus = new SwingTaskStatusDisplay(); playfieldDrawer.setDoubleBuffering(this.useDoubleBuffering); playfieldDrawer.setSyncToScreen(this.syncToScreen); - this.window = new SwingGameWindow(textureRegistry, playfieldDrawer, toolbar, entitySidebar, console); + this.window = new SwingGameWindow(textureRegistry, playfieldDrawer, toolbar, entitySidebar, console, taskStatus); if (this.windowTitle != null) { this.window.setWindowTitle(this.windowTitle); } diff --git a/ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/internal/ConsoleBufferedOutputStream.java b/ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/internal/ConsoleBufferedOutputStream.java index 0db652e17..e664d3c6e 100644 --- a/ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/internal/ConsoleBufferedOutputStream.java +++ b/ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/internal/ConsoleBufferedOutputStream.java @@ -79,7 +79,7 @@ public ConsoleBufferedOutputStream(final JTextPane textPane, final OutputStyle s try { // flush the line buffer in regular intervalls this.flushLineBufferToTextPane(); - } catch (IOException e) { + } catch (final IOException e) { // do not handle exeption during regular buffer flush to avoid exception loops } }); @@ -101,8 +101,8 @@ public void close() throws IOException { @Override public void write(final int character) throws IOException { - char symbol = (char) character; - boolean newline = symbol == '\n'; // should catch CR/LF and LF line endings + final char symbol = (char) character; + final boolean newline = symbol == '\n'; // should catch CR/LF and LF line endings synchronized (this.lineBuffer) { this.lineBuffer.append(symbol); @@ -128,7 +128,7 @@ private void flushLineBufferToTextPane() throws IOException { if (newText.length() > 0) { // new null check because previous check may be obsolete now // print line to text pane try { - StyledDocument content = this.textPane.getStyledDocument(); + final StyledDocument content = this.textPane.getStyledDocument(); synchronized (this.textPane) { content.insertString(content.getLength(), newText, this.style); } diff --git a/ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/internal/SwingGameWindow.java b/ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/internal/SwingGameWindow.java index a5061d624..17672e146 100644 --- a/ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/internal/SwingGameWindow.java +++ b/ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/internal/SwingGameWindow.java @@ -29,6 +29,7 @@ import de.unistuttgart.informatik.fius.icge.ui.GameWindow; import de.unistuttgart.informatik.fius.icge.ui.PlayfieldDrawer; import de.unistuttgart.informatik.fius.icge.ui.SimulationProxy; +import de.unistuttgart.informatik.fius.icge.ui.TaskStatusDisplay; import de.unistuttgart.informatik.fius.icge.ui.TextureRegistry; import de.unistuttgart.informatik.fius.icge.ui.Toolbar; @@ -43,11 +44,12 @@ public class SwingGameWindow extends JFrame implements GameWindow { private static final long serialVersionUID = -7215617949088643819L; - private final SwingTextureRegistry textureRegistry; - private final SwingPlayfieldDrawer playfieldDrawer; - private final SwingToolbar toolbar; - private final SwingEntitySidebar entitySidebar; - private final SwingConsole console; + private final SwingTextureRegistry textureRegistry; + private final SwingPlayfieldDrawer playfieldDrawer; + private final SwingToolbar toolbar; + private final SwingEntitySidebar entitySidebar; + private final SwingConsole console; + private final SwingTaskStatusDisplay taskStatus; /** * Create a new Swing game window using the given submodules. @@ -62,16 +64,19 @@ public class SwingGameWindow extends JFrame implements GameWindow { * The {@link EntitySidebar} to use. * @param console * The {@link Console} to use. + * @param taskStatus + * The {@link TaskStatusDisplay} to use. */ public SwingGameWindow( final SwingTextureRegistry textureRegistry, final SwingPlayfieldDrawer playfieldDrawer, final SwingToolbar toolbar, - final SwingEntitySidebar entitySidebar, final SwingConsole console + final SwingEntitySidebar entitySidebar, final SwingConsole console, final SwingTaskStatusDisplay taskStatus ) { this.textureRegistry = textureRegistry; this.playfieldDrawer = playfieldDrawer; this.toolbar = toolbar; this.entitySidebar = entitySidebar; this.console = console; + this.taskStatus = taskStatus; } @Override @@ -79,7 +84,7 @@ public void setSimulationProxy(final SimulationProxy simulationProxy) { this.playfieldDrawer.setSimulationProxy(simulationProxy); this.toolbar.setSimulationProxy(simulationProxy); this.entitySidebar.setSimulationProxy(simulationProxy); - + this.taskStatus.setSimulationProxy(simulationProxy); } @Override @@ -107,6 +112,11 @@ public Console getConsole() { return this.console; } + @Override + public TaskStatusDisplay getTaskStatusDisplay() { + return this.taskStatus; + } + @Override public void setWindowTitle(final String title) { this.setTitle(title); @@ -155,8 +165,8 @@ public void windowClosing(final WindowEvent e) { // setup bottom pane layout final JTabbedPane bottomPane = new JTabbedPane(SwingConstants.TOP, JTabbedPane.SCROLL_TAB_LAYOUT); bottomPane.addTab("Console", new JScrollPane(consoleComponent)); + bottomPane.addTab("Task Status", this.taskStatus); bottomPane.setPreferredSize(new Dimension(400, 200)); - // TODO task status component: bottomPane.addTab("Task Status", null); // setup JFrame layout this.getContentPane().add(BorderLayout.NORTH, toolbarComponent); diff --git a/ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/internal/SwingTaskStatusDisplay.java b/ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/internal/SwingTaskStatusDisplay.java new file mode 100644 index 000000000..eee399c9a --- /dev/null +++ b/ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/internal/SwingTaskStatusDisplay.java @@ -0,0 +1,180 @@ +/* + * This source file is part of the FIUS ICGE project. + * For more information see github.com/FIUS/ICGE2 + * + * Copyright (c) 2019 the ICGE project authors. + * + * This software is available under the MIT license. + * SPDX-License-Identifier: MIT + */ +package de.unistuttgart.informatik.fius.icge.ui.internal; + +import java.awt.Color; +import java.util.List; +import java.awt.BorderLayout; + +import javax.swing.JButton; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextPane; +import javax.swing.SwingUtilities; +import javax.swing.text.BadLocationException; +import javax.swing.text.DefaultStyledDocument; +import javax.swing.text.Style; +import javax.swing.text.StyleConstants; +import javax.swing.text.StyledDocument; + +import de.unistuttgart.informatik.fius.icge.ui.SimulationProxy; +import de.unistuttgart.informatik.fius.icge.ui.TaskInformation; +import de.unistuttgart.informatik.fius.icge.ui.TaskStatusDisplay; + + +/** + * An implementation of {@link TaskStatusDisplay} using java swing. + * + * @author Fabian Bühler + * @version 1.0 + */ +public class SwingTaskStatusDisplay extends JPanel implements TaskStatusDisplay { + + private static final long serialVersionUID = -2711911902591163118L; + + private final JTextPane textPane; + + private final Style textStyle; + private final Style taskTitle; + private final Style taskSuccess; + private final Style taskFail; + + private SimulationProxy simulationProxy; + + /** + * Default constructor. + */ + public SwingTaskStatusDisplay() { + super(new BorderLayout()); + + // setup text pane + this.textPane = new JTextPane(new DefaultStyledDocument()); + this.textPane.setEditable(false); + + // setup text styles + this.textStyle = this.textPane.addStyle("Text", null); + StyleConstants.setFontFamily(this.textStyle, "serif"); + StyleConstants.setFontSize(this.textStyle, 12); + + this.taskTitle = this.textPane.addStyle("TaskTitle", this.textStyle); + StyleConstants.setBold(this.taskTitle, true); + + this.taskSuccess = this.textPane.addStyle("TaskSuccess", this.taskTitle); + this.taskFail = this.textPane.addStyle("TaskFail", this.taskTitle); + + StyleConstants.setForeground(this.taskSuccess, Color.GREEN); + StyleConstants.setForeground(this.taskFail, Color.RED); + + // setup refresh button + final JButton refreshButton = new JButton("Refresh"); + refreshButton.addActionListener(ae -> { + if (this.simulationProxy != null) { + this.simulationProxy.refreshTaskInformation(); + } + }); + + // pack component + this.add(new JScrollPane(this.textPane), BorderLayout.CENTER); + this.add(refreshButton, BorderLayout.LINE_END); + } + + /** + * Set the simulation proxy. TODO better doc + * + * @param simulationProxy + * The simulation proxy this SwingPlayfieldDrawer should subscribe to + */ + public void setSimulationProxy(final SimulationProxy simulationProxy) { + if (this.simulationProxy != null) throw new IllegalStateException("SimulationProxy is already set and cannot be overwritten!"); + + this.simulationProxy = simulationProxy; + } + + @Override + public void setTaskInformation(final TaskInformation task) { + // invoke later to break out of event thread of the refresh button press handler + SwingUtilities.invokeLater(() -> { + this.textPane.setText(""); // reset document + final StyledDocument document = this.textPane.getStyledDocument(); + if (task != null) { + this.appendTaskInformation(task, document, 0); + } else { + this.appendText(document, "No task set!\n", this.taskTitle); + this.appendText(document, "You can set a task verifier in the SimulationBuilder.", this.textStyle); + } + }); + } + + /** + * Appends the task information of the task and all subtasks to the styled document. + * + * @param task + * the task information to add + * @param document + * the document to append the information to + * @param depth + * the current task depth (starts with 0, may be used to indent subtasks later) + */ + private void appendTaskInformation(final TaskInformation task, final StyledDocument document, final int depth) { + + // append title + String title = task.getTaskTitle(); + if (title == null || title.length() == 0) { + title = "Unnamed Task"; + } + this.appendText(document, title, this.taskTitle); + + // append task status + switch (task.getTaskStatus()) { + case SUCCESSFUL: + this.appendText(document, " (success)\n", this.taskSuccess); + break; + case FAILED: + this.appendText(document, " (error)\n", this.taskFail); + break; + case UNDECIDED: // don't add any text for undecided status (same as default) + default: + this.appendText(document, "\n", this.taskTitle); + } + + // append description + final String description = task.getTaskDescription(); + if (description != null && description.length() > 0) { + this.appendText(document, description + '\n', this.textStyle); + } + + // handle subtasks + final List childTasks = task.getChildTasks(); + if (childTasks != null) { + for (final TaskInformation subTask : childTasks) { + this.appendText(document, "\n", this.textStyle); + this.appendTaskInformation(subTask, document, depth + 1); + } + } + } + + /** + * Appends text to a styled document while silently dismissing {@link BadLocationException}. + * + * @param document + * the document to append to + * @param text + * the text to append + * @param style + * the style of the text to append + */ + private void appendText(final StyledDocument document, final String text, final Style style) { + try { + document.insertString(document.getLength(), text, style); + } catch (final BadLocationException e) { + e.printStackTrace(); + } + } +} diff --git a/ICGE-Ui/src/test/java/de/unistuttgart/informatik/fius/icge/ui/SwingGameWindowUiTest.java b/ICGE-Ui/src/test/java/de/unistuttgart/informatik/fius/icge/ui/SwingGameWindowUiTest.java index 6f07a44c1..11e84b9bb 100644 --- a/ICGE-Ui/src/test/java/de/unistuttgart/informatik/fius/icge/ui/SwingGameWindowUiTest.java +++ b/ICGE-Ui/src/test/java/de/unistuttgart/informatik/fius/icge/ui/SwingGameWindowUiTest.java @@ -18,6 +18,7 @@ import de.unistuttgart.informatik.fius.icge.ui.internal.SwingEntitySidebar; import de.unistuttgart.informatik.fius.icge.ui.internal.SwingGameWindow; import de.unistuttgart.informatik.fius.icge.ui.internal.SwingPlayfieldDrawer; +import de.unistuttgart.informatik.fius.icge.ui.internal.SwingTaskStatusDisplay; import de.unistuttgart.informatik.fius.icge.ui.internal.SwingTextureRegistry; import de.unistuttgart.informatik.fius.icge.ui.internal.SwingToolbar; @@ -41,8 +42,9 @@ public void setup() { final SwingToolbar toolbar = new SwingToolbar(textureRegistry); final SwingEntitySidebar entitySidebar = new SwingEntitySidebar(textureRegistry); final SwingConsole console = new SwingConsole(); + final SwingTaskStatusDisplay taskStatus = new SwingTaskStatusDisplay(); - this.window = new SwingGameWindow(textureRegistry, playfieldDrawer, toolbar, entitySidebar, console); + this.window = new SwingGameWindow(textureRegistry, playfieldDrawer, toolbar, entitySidebar, console, taskStatus); } /** From 9b8212a06c5624b7408de9ae559df22b6aecaee2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20B=C3=BChler?= <17296905+buehlefs@users.noreply.github.com> Date: Sun, 19 Jul 2020 19:22:43 +0200 Subject: [PATCH 2/4] Refactor TestTaskVerifier and add better documentation --- .../icge/manualstart/TestTaskVerifier.java | 41 ++++++++++++------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/ICGE-ManualStart/src/main/java/de/unistuttgart/informatik/fius/icge/manualstart/TestTaskVerifier.java b/ICGE-ManualStart/src/main/java/de/unistuttgart/informatik/fius/icge/manualstart/TestTaskVerifier.java index 117c8420c..49a9efaf1 100644 --- a/ICGE-ManualStart/src/main/java/de/unistuttgart/informatik/fius/icge/manualstart/TestTaskVerifier.java +++ b/ICGE-ManualStart/src/main/java/de/unistuttgart/informatik/fius/icge/manualstart/TestTaskVerifier.java @@ -22,13 +22,18 @@ /** * Example task verifier. * + * Verifies that between {@code minStepsToWalk} and {@code maxStepsToWalk} {@code EntityMoveAction} events are in the + * log of the simulation. Below the task is UNDECIDED and above the task is FAILED. + * * @author Fabian Bühler */ public class TestTaskVerifier implements TaskVerifier, TaskInformation { private ActionLog log; - private TaskVerificationStatus taskIsValid = TaskVerificationStatus.UNDECIDED; - private int stepsToWalk = 4; + private TaskVerificationStatus taskIsValid = TaskVerificationStatus.UNDECIDED; + private int minStepsToWalk = 4; + private int maxStepsToWalk = 14; + private int stepsWalked = 0; @Override public void attachToSimulation(Simulation sim) { @@ -42,14 +47,22 @@ public void verify() { return; } // check the number of steps/moves all entity have taken together - int steps = this.log.getActionsOfType(EntityMoveAction.class, true).size(); - if (steps > 3) { - this.stepsToWalk = 0; - this.taskIsValid = TaskVerificationStatus.SUCCESSFUL; - } else { - this.stepsToWalk = 4 - steps; + this.stepsWalked = this.log.getActionsOfType(EntityMoveAction.class, true).size(); + + // as long as task is still achievable use UNDECIDED status + if (this.stepsWalked < this.minStepsToWalk) { + this.taskIsValid = TaskVerificationStatus.UNDECIDED; + return; + } + + // if task is failed irreversibly use FAILED status + if (this.stepsWalked > this.maxStepsToWalk) { this.taskIsValid = TaskVerificationStatus.FAILED; + return; } + + // use SUCCESSFUL status if task is solved correctly + this.taskIsValid = TaskVerificationStatus.SUCCESSFUL; } @Override @@ -64,12 +77,12 @@ public String getTaskTitle() { @Override public String getTaskDescription() { - String description = "Just a demo task to test the UI and the Backend."; - if (this.stepsToWalk > 1) { - description += " (" + this.stepsToWalk + " steps to walk)"; - } - if (this.stepsToWalk == 1) { - description += " (1 step to walk)"; + String description = "Just a demo task to test the UI and the Backend.\n"; + description += "Walk between " + this.minStepsToWalk + " and " + this.maxStepsToWalk + " steps to solve this task."; + if (this.stepsWalked != 1) { // computing hints is fine if they only change when verify is called + description += " (" + this.stepsWalked + " steps walked)"; + } else { + description += " (1 step walked)"; } return description; } From 1cd3194ee6febfdca1e1dee0786af9195539a86e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20B=C3=BChler?= <17296905+buehlefs@users.noreply.github.com> Date: Sun, 19 Jul 2020 19:23:26 +0200 Subject: [PATCH 3/4] Fix how task status is displayed (changed wording) --- .../fius/icge/ui/internal/SwingTaskStatusDisplay.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/internal/SwingTaskStatusDisplay.java b/ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/internal/SwingTaskStatusDisplay.java index eee399c9a..b64b610cb 100644 --- a/ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/internal/SwingTaskStatusDisplay.java +++ b/ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/internal/SwingTaskStatusDisplay.java @@ -137,11 +137,11 @@ private void appendTaskInformation(final TaskInformation task, final StyledDocum this.appendText(document, " (success)\n", this.taskSuccess); break; case FAILED: - this.appendText(document, " (error)\n", this.taskFail); + this.appendText(document, " (failed)\n", this.taskFail); break; case UNDECIDED: // don't add any text for undecided status (same as default) default: - this.appendText(document, "\n", this.taskTitle); + this.appendText(document, " (pending)\n", this.taskTitle); } // append description From 391deafa6945803a3f7ea28204d48e06713b0304 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20B=C3=BChler?= <17296905+buehlefs@users.noreply.github.com> Date: Sun, 19 Jul 2020 19:38:08 +0200 Subject: [PATCH 4/4] Fix usage of null instead of empty list --- .../informatik/fius/icge/manualstart/TestTaskVerifier.java | 3 ++- .../unistuttgart/informatik/fius/icge/ui/TaskInformation.java | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ICGE-ManualStart/src/main/java/de/unistuttgart/informatik/fius/icge/manualstart/TestTaskVerifier.java b/ICGE-ManualStart/src/main/java/de/unistuttgart/informatik/fius/icge/manualstart/TestTaskVerifier.java index 49a9efaf1..3214595b5 100644 --- a/ICGE-ManualStart/src/main/java/de/unistuttgart/informatik/fius/icge/manualstart/TestTaskVerifier.java +++ b/ICGE-ManualStart/src/main/java/de/unistuttgart/informatik/fius/icge/manualstart/TestTaskVerifier.java @@ -9,6 +9,7 @@ */ package de.unistuttgart.informatik.fius.icge.manualstart; +import java.util.Collections; import java.util.List; import de.unistuttgart.informatik.fius.icge.simulation.Simulation; @@ -94,7 +95,7 @@ public TaskVerificationStatus getTaskStatus() { @Override public List getChildTasks() { - return null; + return Collections.emptyList(); } } diff --git a/ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/TaskInformation.java b/ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/TaskInformation.java index 58cc3fa9c..0808f4e69 100644 --- a/ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/TaskInformation.java +++ b/ICGE-Ui/src/main/java/de/unistuttgart/informatik/fius/icge/ui/TaskInformation.java @@ -44,7 +44,7 @@ public interface TaskInformation { /** * Get a list of child/sub tasks of this task. * - * @return a list of sub-tasks (may be {@code null} or empty) + * @return a list of sub-tasks (must not be {@code null}, use {@code Collections.emptyList()} instead) */ List getChildTasks(); }