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 b72c3326c..736866a56 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 @@ -9,6 +9,8 @@ */ package de.unistuttgart.informatik.fius.icge.simulation; +import de.unistuttgart.informatik.fius.icge.simulation.entity.program.EntityProgramRegistry; +import de.unistuttgart.informatik.fius.icge.simulation.entity.program.EntityProgramRunner; import de.unistuttgart.informatik.fius.icge.ui.UiManager; @@ -39,6 +41,20 @@ public interface Simulation { */ SimulationClock getSimulationClock(); + /** + * Get the entity program registry for this simulation. + * + * @return the entity program registry used by this simulation + */ + EntityProgramRegistry getEntityProgramRegistry(); + + /** + * Get the entity program runner for this simulation. + * + * @return the entity program runner used by this simulation + */ + EntityProgramRunner getEntityProgramRunner(); + /** * Initialize the simulation and all its submodules. */ diff --git a/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/SimulationClock.java b/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/SimulationClock.java index 3979a92d8..740781254 100644 --- a/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/SimulationClock.java +++ b/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/SimulationClock.java @@ -103,7 +103,9 @@ public interface SimulationClock { * @param tick * The absolute number of the tick at which the operation will be run * @param endOfOperation - * Tick processing will be halted until this future is completed + * Tick processing will be halted until this future is completed; must not complete exceptionally + * @throws IllegalStateException + * if the end of operation completes exceptionally */ void scheduleOperationAtTick(long tick, CompletableFuture endOfOperation); @@ -117,7 +119,9 @@ public interface SimulationClock { * @param ticks * The number of ticks until the tick, for which to schedule the operation * @param endOfOperation - * Tick processing will be halted until this future is completed + * Tick processing will be halted until this future is completed; must not complete exceptionally + * @throws IllegalStateException + * if the end of operation completes exceptionally */ void scheduleOperationInTicks(long ticks, CompletableFuture endOfOperation); @@ -126,7 +130,9 @@ public interface SimulationClock { * processing will halt until the given end of operation is completed. * * @param endOfOperation - * Tick processing will be halted until this future is completed + * Tick processing will be halted until this future is completed; must not complete exceptionally + * @throws IllegalStateException + * if the end of operation completes exceptionally */ void scheduleOperationAtNextTick(CompletableFuture endOfOperation); } diff --git a/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/SimulationFactory.java b/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/SimulationFactory.java index 7ab43916b..3487cf246 100644 --- a/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/SimulationFactory.java +++ b/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/SimulationFactory.java @@ -11,6 +11,8 @@ import de.unistuttgart.informatik.fius.icge.simulation.internal.StandardSimulation; import de.unistuttgart.informatik.fius.icge.simulation.internal.StandardSimulationClock; +import de.unistuttgart.informatik.fius.icge.simulation.internal.entity.program.StandardEntityProgramRegistry; +import de.unistuttgart.informatik.fius.icge.simulation.internal.entity.program.StandardEntityProgramRunner; import de.unistuttgart.informatik.fius.icge.simulation.internal.playfield.StandardPlayfield; import de.unistuttgart.informatik.fius.icge.ui.UiManager; import de.unistuttgart.informatik.fius.icge.ui.UiManagerFactory; @@ -32,7 +34,9 @@ public static Simulation createSimulation() { StandardPlayfield playfield = new StandardPlayfield(); StandardSimulationClock tickManager = new StandardSimulationClock(); + StandardEntityProgramRegistry entityProgramRegistry = new StandardEntityProgramRegistry(); + StandardEntityProgramRunner entityProgramRunner = new StandardEntityProgramRunner(entityProgramRegistry); - return new StandardSimulation(uiManager, playfield, tickManager); + return new StandardSimulation(uiManager, playfield, tickManager, entityProgramRegistry, entityProgramRunner); } } diff --git a/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/entity/program/EntityProgram.java b/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/entity/program/EntityProgram.java new file mode 100644 index 000000000..6ec2cc276 --- /dev/null +++ b/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/entity/program/EntityProgram.java @@ -0,0 +1,40 @@ +/* + * 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.entity.program; + +import de.unistuttgart.informatik.fius.icge.simulation.entity.Entity; + + +/** + * A program to be executed by an entity + * + * @author Tim Neumann + */ +public interface EntityProgram { + + /** + * Run this program on the given entity. + * + * @param entity + * The entity to run this program on + * @throws IllegalArgumentException + * if this program cannot run on the given entity + */ + void run(Entity entity); + + /** + * Check whether this program could run on the given entity. + * + * @param entity + * The entity to check + * @return true if this program could run on that entity + */ + boolean canRunOn(Entity entity); +} diff --git a/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/entity/program/EntityProgramRegistry.java b/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/entity/program/EntityProgramRegistry.java new file mode 100644 index 000000000..2cee40f5e --- /dev/null +++ b/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/entity/program/EntityProgramRegistry.java @@ -0,0 +1,90 @@ +/* + * 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.entity.program; + +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.function.Supplier; + +import de.unistuttgart.informatik.fius.icge.simulation.entity.Entity; +import de.unistuttgart.informatik.fius.icge.simulation.exception.ElementExistsException; + + +/** + * The registry for all {@link EntityProgram}s. + * + * @author Tim Neumann + */ +public interface EntityProgramRegistry { + /** + * Register an entity program with the given name. + * + * @param name + * the name of the program; must not be null; must be unique + * + * @param program + * the program to register; must not be null + * + * @throws IllegalArgumentException + * if an argument is null + * @throws ElementExistsException + * if the name is already used + */ + void registerEntityProgram(String name, EntityProgram program); + + /** + * Register many entity programs of the same type with the given name. + * + * @param name + * the name for these programs; must not be null; must be unique + * + * @param programGenerator + * a generator for the type of program; must not be null; must provide non null programs + * + * @throws IllegalArgumentException + * if an argument is null + * + * @throws ElementExistsException + * if the name is already used + */ + void registerManyEntityProgram(String name, Supplier programGenerator); + + /** + * Get the names of all registered entity programs. + * + * @return a set of the names of all registered entity programs + */ + Set getPrograms(); + + /** + * Get the names of all entity programs, which could run on the given entity. + * + * @param entity + * the entity to get the programs for; must not be null + * @return a set of the names of all entity programs which could be run on this entity + * + * @throws IllegalArgumentException + * if an argument is null + */ + Set getProgramsForEntity(Entity entity); + + /** + * Get the entity program for a given name. + * + * @param name + * The name to get the program for; must not be null + * @return The program for the given name + * @throws IllegalArgumentException + * if an argument is null + * @throws NoSuchElementException + * if the given name is not registered + */ + EntityProgram getEntityProgram(String name); +} diff --git a/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/entity/program/EntityProgramRunner.java b/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/entity/program/EntityProgramRunner.java new file mode 100644 index 000000000..b02a49456 --- /dev/null +++ b/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/entity/program/EntityProgramRunner.java @@ -0,0 +1,96 @@ +/* + * 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.entity.program; + +import java.util.NoSuchElementException; + +import de.unistuttgart.informatik.fius.icge.simulation.entity.Entity; +import de.unistuttgart.informatik.fius.icge.simulation.exception.CannotRunProgramException; + + +/** + * A class to run {@link EntityProgram}s. + * + * @author Tim Neumann + */ +public interface EntityProgramRunner { + + /** + * Get the state of an entity program. + * + * @param program + * The name of program to get the state of; must be a registered program in the {@link EntityProgramRegistry} + * @return The state of the given program + * @throws NoSuchElementException + * if the program is not registered in the {@link EntityProgramRegistry} + * @throws IllegalArgumentException + * if an argument is null + */ + public EntityProgramState getState(String program); + + /** + * Check whether the given program can run. + * + * @param program + * The name of program to check; must be a registered program in the {@link EntityProgramRegistry} + * + * @return true if the program can run + * @throws NoSuchElementException + * if the program is not registered in the {@link EntityProgramRegistry} + * @throws IllegalArgumentException + * if an argument is null + */ + public boolean canRunProgram(String program); + + /** + * Check whether the given program can be run to the given entity. + *

+ * First checks {@link #canRunProgram(String)} and then {@link EntityProgram#canRunOn(Entity)}. + *

+ * + * @param program + * The name of the program to check; must be a registered program in the {@link EntityProgramRegistry} + * @param entity + * The entity to check; must not be null + * @return true if the program can run on that entity + * @throws NoSuchElementException + * if the program is not registered in the {@link EntityProgramRegistry} + * @throws IllegalArgumentException + * if an argument is null + */ + public boolean canRunProgramOn(String program, Entity entity); + + /** + * Run an entity program on an entity. + * + * @param program + * The program to run; must be a registered program in the {@link EntityProgramRegistry}; must be able to run on + * the entity now + * @param entity + * The entity to run the program on + * @throws IllegalArgumentException + * if an argument is null + * @throws NoSuchElementException + * if the program is not registered in the {@link EntityProgramRegistry} + * @throws CannotRunProgramException + * if the program is not able to run on the entity now + * @throws RuntimeException + * if an exception occurred in the entity program + */ + public void run(String program, Entity entity); + + /** + * Force stop all running entity programs. + *

+ * This does not block until successful shutdown + *

+ */ + public void forceStop(); +} diff --git a/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/entity/program/EntityProgramState.java b/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/entity/program/EntityProgramState.java new file mode 100644 index 000000000..5671bf750 --- /dev/null +++ b/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/entity/program/EntityProgramState.java @@ -0,0 +1,26 @@ +/* + * 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.entity.program; + +/** + * The state of a entity program + * + * @author Tim Neumann + */ +public enum EntityProgramState { + /** When the entity program is new and was not started yet. */ + NEW, + /** When the entity program is currently running. */ + RUNNING, + /** When the entity program finished nominally. */ + FINISHED, + /** When the entity program was killed by the {@link EntityProgramRunner}. */ + KILLED +} diff --git a/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/exception/CannotRunProgramException.java b/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/exception/CannotRunProgramException.java new file mode 100644 index 000000000..11576adf7 --- /dev/null +++ b/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/exception/CannotRunProgramException.java @@ -0,0 +1,73 @@ +/* + * 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.exception; + +/** + * This exception indicates that an entity program cannot be run. + * + * @author Tim Neumann + */ +public class CannotRunProgramException extends IllegalArgumentException { + + /** + * generated + */ + private static final long serialVersionUID = -5283363131395011240L; + + /** + * Constructs an CannotRunProgramException with no detail message. + */ + public CannotRunProgramException() { + super(); + } + + /** + * Constructs an CannotRunProgramException with the specified detail message. + * + * @param s + * the detail message. + */ + public CannotRunProgramException(String s) { + super(s); + } + + /** + * Constructs a new exception with the specified detail message and cause. + * + *

+ * Note that the detail message associated with cause is not automatically incorporated in this + * exception's detail message. + * + * @param message + * the detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method). + * @param cause + * the cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A {@code null} + * value is permitted, and indicates that the cause is nonexistent or unknown.) + * @since 1.5 + */ + public CannotRunProgramException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructs a new exception with the specified cause and a detail message of + * {@code (cause==null ? null : cause.toString())} (which typically contains the class and detail message of + * {@code cause}). This constructor is useful for exceptions that are little more than wrappers for other throwables + * (for example, {@link java.security.PrivilegedActionException}). + * + * @param cause + * the cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A {@code null} + * value is permitted, and indicates that the cause is nonexistent or unknown.) + * @since 1.5 + */ + public CannotRunProgramException(Throwable cause) { + super(cause); + } +} diff --git a/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/exception/ElementExistsException.java b/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/exception/ElementExistsException.java new file mode 100644 index 000000000..946ab1844 --- /dev/null +++ b/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/exception/ElementExistsException.java @@ -0,0 +1,40 @@ +/* + * 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.exception; + +/** + * This exception indicates that an entity program cannot be run. + * + * @author Tim Neumann + */ +public class ElementExistsException extends IllegalArgumentException { + + /** + * generated + */ + private static final long serialVersionUID = -564159508677510779L; + + /** + * Constructs an ElementExistsException with no detail message. + */ + public ElementExistsException() { + super(); + } + + /** + * Constructs an ElementExistsException with the specified detail message. + * + * @param s + * the detail message. + */ + public ElementExistsException(String s) { + super(s); + } +} diff --git a/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/exception/UncheckedInterruptedException.java b/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/exception/UncheckedInterruptedException.java new file mode 100644 index 000000000..c23ed9c48 --- /dev/null +++ b/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/exception/UncheckedInterruptedException.java @@ -0,0 +1,73 @@ +/* + * 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.exception; + +/** + * This exception indicates that an entity program cannot be run. + * + * @author Tim Neumann + */ +public class UncheckedInterruptedException extends IllegalArgumentException { + + /** + * generated + */ + private static final long serialVersionUID = -8407441370711369840L; + + /** + * Constructs an EntityProgramInterruptedException with no detail message. + */ + public UncheckedInterruptedException() { + super(); + } + + /** + * Constructs an EntityProgramInterruptedException with the specified detail message. + * + * @param s + * the detail message. + */ + public UncheckedInterruptedException(String s) { + super(s); + } + + /** + * Constructs a new exception with the specified detail message and cause. + * + *

+ * Note that the detail message associated with cause is not automatically incorporated in this + * exception's detail message. + * + * @param message + * the detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method). + * @param cause + * the cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A {@code null} + * value is permitted, and indicates that the cause is nonexistent or unknown.) + * @since 1.5 + */ + public UncheckedInterruptedException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructs a new exception with the specified cause and a detail message of + * {@code (cause==null ? null : cause.toString())} (which typically contains the class and detail message of + * {@code cause}). This constructor is useful for exceptions that are little more than wrappers for other throwables + * (for example, {@link java.security.PrivilegedActionException}). + * + * @param cause + * the cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A {@code null} + * value is permitted, and indicates that the cause is nonexistent or unknown.) + * @since 1.5 + */ + public UncheckedInterruptedException(Throwable cause) { + super(cause); + } +} 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 161c47343..34f5d5192 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,10 @@ 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.entity.program.EntityProgramRegistry; +import de.unistuttgart.informatik.fius.icge.simulation.entity.program.EntityProgramRunner; +import de.unistuttgart.informatik.fius.icge.simulation.internal.entity.program.StandardEntityProgramRegistry; +import de.unistuttgart.informatik.fius.icge.simulation.internal.entity.program.StandardEntityProgramRunner; import de.unistuttgart.informatik.fius.icge.simulation.internal.playfield.StandardPlayfield; import de.unistuttgart.informatik.fius.icge.ui.UiManager; @@ -26,6 +30,8 @@ public class StandardSimulation implements Simulation { private final UiManager uiManager; private final StandardPlayfield playfield; private final StandardSimulationClock tickManager; + private final StandardEntityProgramRegistry entityProgramRegistry; + private final StandardEntityProgramRunner entityProgramRunner; /** * Creates a new standard simulation with the given parameters. @@ -36,11 +42,20 @@ public class StandardSimulation implements Simulation { * The playfield to use * @param tickManager * The tickManager to use + * @param entityProgramRegistry + * The entityProgramRegistry to use + * @param entityProgramRunner + * The entityProgramRunner to use */ - public StandardSimulation(UiManager uiManager, StandardPlayfield playfield, StandardSimulationClock tickManager) { + public StandardSimulation( + UiManager uiManager, StandardPlayfield playfield, StandardSimulationClock tickManager, + StandardEntityProgramRegistry entityProgramRegistry, StandardEntityProgramRunner entityProgramRunner + ) { this.uiManager = uiManager; this.playfield = playfield; this.tickManager = tickManager; + this.entityProgramRegistry = entityProgramRegistry; + this.entityProgramRunner = entityProgramRunner; } @Override @@ -68,4 +83,14 @@ public SimulationClock getSimulationClock() { return this.tickManager; } + @Override + public EntityProgramRegistry getEntityProgramRegistry() { + return this.entityProgramRegistry; + } + + @Override + public EntityProgramRunner getEntityProgramRunner() { + return this.entityProgramRunner; + } + } diff --git a/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/internal/StandardSimulationClock.java b/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/internal/StandardSimulationClock.java index b23830bc9..9f64266bf 100644 --- a/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/internal/StandardSimulationClock.java +++ b/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/internal/StandardSimulationClock.java @@ -14,10 +14,12 @@ import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import java.util.function.Function; import de.unistuttgart.informatik.fius.icge.simulation.Simulation; import de.unistuttgart.informatik.fius.icge.simulation.SimulationClock; +import de.unistuttgart.informatik.fius.icge.simulation.exception.UncheckedInterruptedException; import de.unistuttgart.informatik.fius.icge.ui.PlayfieldDrawer; @@ -147,12 +149,26 @@ public void scheduleOperationAtTick(long tick, CompletableFuture endOfOper registerTickListener(tickNumber -> { if (tickNumber >= tick) { startOfOperation.complete(null); - endOfOperation.join(); + try { + endOfOperation.get(); + } catch (Exception e) { + throw new RuntimeException(e); + } return false; } return true; }); - startOfOperation.join(); + try { + startOfOperation.get(); + } catch (InterruptedException e) { + throw new UncheckedInterruptedException(e); + } catch (ExecutionException e) { + Throwable cause = e.getCause().getCause(); + if (cause instanceof InterruptedException) { + throw new UncheckedInterruptedException(cause); + } + throw new IllegalStateException("The end of operation completed exceptionally", cause); + } } @Override diff --git a/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/internal/entity/program/EntityProgramRegistryEntry.java b/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/internal/entity/program/EntityProgramRegistryEntry.java new file mode 100644 index 000000000..986e179de --- /dev/null +++ b/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/internal/entity/program/EntityProgramRegistryEntry.java @@ -0,0 +1,102 @@ +/* + * 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.internal.entity.program; + +import java.util.function.Supplier; + +import de.unistuttgart.informatik.fius.icge.simulation.entity.program.EntityProgram; +import de.unistuttgart.informatik.fius.icge.simulation.entity.program.EntityProgramRegistry; + + +/** + * Objects of this class store information about an {@link EntityProgram} needed by the {@link EntityProgramRegistry}. + * + * @author Tim Neumann + */ +public class EntityProgramRegistryEntry { + private final String name; + private final boolean single; + private final EntityProgram program; + private final Supplier programGenerator; + + /** + * Create a new info for a single entity program + * + * @param name + * The name for the program; cannot be null + * @param program + * The program; cannot be null + * @throws IllegalArgumentException + * if an argument is null + */ + public EntityProgramRegistryEntry(final String name, final EntityProgram program) { + if ((name == null) || (program == null)) throw new IllegalArgumentException("Argument cannot be null."); + this.name = name; + this.single = true; + this.program = program; + this.programGenerator = null; + } + + /** + * Create a new info for many entity programs provided by the given generator + * + * @param name + * The name for this type of program; cannot be null + * @param programGenerator + * A generator for this type of program; cannot be null; must provide non null programs + * @throws IllegalArgumentException + * if an argument is null + */ + public EntityProgramRegistryEntry(final String name, final Supplier programGenerator) { + if ((name == null) || (programGenerator == null)) throw new IllegalArgumentException("Argument cannot be null."); + this.name = name; + this.single = false; + this.program = null; + this.programGenerator = programGenerator; + } + + /** + * @return the name of this program + */ + public String getName() { + return this.name; + } + + /** + * Get the program instance of this info. + *

+ * If this is a info about many programs, calls the get method of the generator. + *

+ * @return the program instance + */ + public EntityProgram getProgram() { + if(this.single) return this.program; + EntityProgram prog = this.programGenerator.get(); + if (prog == null) throw new IllegalStateException("Program Generator returned null."); + return prog; + } + + @Override + public int hashCode() { + if (this.single) return this.name.hashCode() + this.program.hashCode(); + return this.name.hashCode() + this.programGenerator.hashCode(); + } + + @Override + public boolean equals(final Object obj) { + if (!(obj instanceof EntityProgramRegistryEntry)) return false; + final EntityProgramRegistryEntry other = (EntityProgramRegistryEntry) obj; + if (this.single != other.single) return false; + if (!this.name.equals(other.name)) return false; + if (this.single) return this.program.equals(other.program); + return this.programGenerator.equals(other.programGenerator); + } + +} diff --git a/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/internal/entity/program/EntityProgramRunningInfo.java b/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/internal/entity/program/EntityProgramRunningInfo.java new file mode 100644 index 000000000..0c9a5143d --- /dev/null +++ b/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/internal/entity/program/EntityProgramRunningInfo.java @@ -0,0 +1,88 @@ +/* + * 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.internal.entity.program; + +import de.unistuttgart.informatik.fius.icge.simulation.entity.program.EntityProgram; +import de.unistuttgart.informatik.fius.icge.simulation.entity.program.EntityProgramState; + + +/** + * An object holding information about the execution of a entity program. + * @author Tim Neumann + */ +public class EntityProgramRunningInfo { + private EntityProgramState state; + private final EntityProgram program; + private Thread thread; + + /** + * Initialize + * + * @param program + * the program for this object; must not be null + * @throws IllegalArgumentException + * if the argument is null + */ + public EntityProgramRunningInfo(EntityProgram program) { + if (program == null) throw new IllegalArgumentException("Argument is null."); + + this.program = program; + this.state = EntityProgramState.NEW; + } + + /** + * @return the state of this object; cannot be null + */ + public EntityProgramState getState() { + return this.state; + } + + /** + * Set the state of this object + * + * @param state + * the new state; must not be null + * @throws IllegalArgumentException + * if the argument is null + */ + public void setState(EntityProgramState state) { + if (state == null) throw new IllegalArgumentException("Argument is null."); + this.state = state; + } + + /** + * Get the program of this object + * + * @return program the program of this object; cannot be null + */ + public EntityProgram getProgram() { + return this.program; + } + + /** + * Get the thread of this object + * + * @return the thread of this object; can be null + */ + public Thread getThread() { + return this.thread; + } + + /** + * Set the thread of this object + * + * @param thread + * the new thread; may be null + */ + public void setThread(Thread thread) { + this.thread = thread; + } + +} diff --git a/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/internal/entity/program/StandardEntityProgramRegistry.java b/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/internal/entity/program/StandardEntityProgramRegistry.java new file mode 100644 index 000000000..ff18d2564 --- /dev/null +++ b/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/internal/entity/program/StandardEntityProgramRegistry.java @@ -0,0 +1,71 @@ +/* + * 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.internal.entity.program; + +import java.util.HashMap; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import de.unistuttgart.informatik.fius.icge.simulation.entity.Entity; +import de.unistuttgart.informatik.fius.icge.simulation.entity.program.EntityProgram; +import de.unistuttgart.informatik.fius.icge.simulation.entity.program.EntityProgramRegistry; +import de.unistuttgart.informatik.fius.icge.simulation.exception.ElementExistsException; + + +/** + * The standard implementation of {@link EntityProgramRegistry} + * + * @author Tim Neumann + */ +public class StandardEntityProgramRegistry implements EntityProgramRegistry { + + private Map programs = new HashMap<>(); + + + @Override + public void registerEntityProgram(String name, EntityProgram program) { + if (name == null || program == null) throw new IllegalArgumentException("An argument is null."); + + if (this.programs.containsKey(name)) throw new ElementExistsException("Name is already in use."); + + this.programs.put(name, new EntityProgramRegistryEntry(name, program)); + } + + @Override + public void registerManyEntityProgram(String name, Supplier programGenerator) { + if (name == null || programGenerator == null) throw new IllegalArgumentException("An argument is null."); + + if (this.programs.containsKey(name)) throw new ElementExistsException("Name is already in use."); + + this.programs.put(name, new EntityProgramRegistryEntry(name, programGenerator)); + } + + @Override + public Set getPrograms() { + return Set.copyOf(this.programs.keySet()); + } + + @Override + public Set getProgramsForEntity(Entity entity) { + return this.programs.entrySet().stream().filter(entry -> entry.getValue().getProgram().canRunOn(entity)) + .map(entry -> entry.getKey()).collect(Collectors.toSet()); + } + + @Override + public EntityProgram getEntityProgram(String name) { + if (name == null) throw new IllegalArgumentException("An argument is null."); + if (!this.programs.containsKey(name)) throw new NoSuchElementException(); + return this.programs.get(name).getProgram(); + } + +} diff --git a/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/internal/entity/program/StandardEntityProgramRunner.java b/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/internal/entity/program/StandardEntityProgramRunner.java new file mode 100644 index 000000000..b90436283 --- /dev/null +++ b/ICGE-Simulation/src/main/java/de/unistuttgart/informatik/fius/icge/simulation/internal/entity/program/StandardEntityProgramRunner.java @@ -0,0 +1,108 @@ +/* + * 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.internal.entity.program; + +import java.util.HashMap; +import java.util.Map; + +import de.unistuttgart.informatik.fius.icge.simulation.entity.Entity; +import de.unistuttgart.informatik.fius.icge.simulation.entity.program.EntityProgramRegistry; +import de.unistuttgart.informatik.fius.icge.simulation.entity.program.EntityProgramRunner; +import de.unistuttgart.informatik.fius.icge.simulation.entity.program.EntityProgramState; +import de.unistuttgart.informatik.fius.icge.simulation.exception.CannotRunProgramException; +import de.unistuttgart.informatik.fius.icge.simulation.exception.UncheckedInterruptedException; + + +/** + * The standard implementation of {@link EntityProgramRunner}. + * + * @author Tim Neumann + */ +public class StandardEntityProgramRunner implements EntityProgramRunner { + + private final EntityProgramRegistry registry; + + private Map programs = new HashMap<>(); + + /** + * Create a new StandardEntityProgramRunner. + * + * @param registry + * The EntityProgramRegistry to use + */ + public StandardEntityProgramRunner(EntityProgramRegistry registry) { + this.registry = registry; + } + + private EntityProgramRunningInfo getInfo(String program) { + if (!this.programs.containsKey(program)) { + this.programs.put(program, new EntityProgramRunningInfo(this.registry.getEntityProgram(program))); + } + return this.programs.get(program); + } + + @Override + public EntityProgramState getState(String program) { + if (program == null) throw new IllegalArgumentException("Argument is null."); + return getInfo(program).getState(); + } + + private boolean canRunProgram(EntityProgramRunningInfo info) { + return info.getState() == EntityProgramState.NEW; + } + + @Override + public boolean canRunProgram(String program) { + if (program == null) throw new IllegalArgumentException("Argument is null."); + return canRunProgram(getInfo(program)); + } + + private boolean canRunProgramOn(EntityProgramRunningInfo info, Entity entity) { + if (!canRunProgram(info)) return false; + return info.getProgram().canRunOn(entity); + } + + @Override + public boolean canRunProgramOn(String program, Entity entity) { + if (program == null || entity == null) throw new IllegalArgumentException("Argument is null."); + return canRunProgramOn(getInfo(program), entity); + } + + @Override + public void run(String program, Entity entity) { + if (program == null || entity == null) throw new IllegalArgumentException("Argument is null."); + + var info = getInfo(program); + + if (!canRunProgramOn(info, entity)) throw new CannotRunProgramException(); + + String threadName = "EntityProgramRunner" + "_" + program + "_on_" + entity.toString(); + Thread thread = new Thread(() -> { + try { + info.getProgram().run(entity); + info.setState(EntityProgramState.FINISHED); + } catch (@SuppressWarnings("unused") UncheckedInterruptedException e) { + info.setState(EntityProgramState.KILLED); + } + }, threadName); + + info.setThread(thread); + info.setState(EntityProgramState.RUNNING); + thread.start(); + } + + @Override + public void forceStop() { + for (EntityProgramRunningInfo info : this.programs.values()) { + info.getThread().interrupt(); + } + } + +} diff --git a/ICGE-Simulation/src/main/java/module-info.java b/ICGE-Simulation/src/main/java/module-info.java index 52dd1117d..04bbddab4 100644 --- a/ICGE-Simulation/src/main/java/module-info.java +++ b/ICGE-Simulation/src/main/java/module-info.java @@ -3,5 +3,6 @@ exports de.unistuttgart.informatik.fius.icge.simulation; exports de.unistuttgart.informatik.fius.icge.simulation.entity; + exports de.unistuttgart.informatik.fius.icge.simulation.entity.program; exports de.unistuttgart.informatik.fius.icge.simulation.exception; } diff --git a/examples/mario/src/main/java/de/unistuttgart/informatik/fius/icge/example/mario/Main.java b/examples/mario/src/main/java/de/unistuttgart/informatik/fius/icge/example/mario/Main.java index 30c7d9085..7e74cd2dc 100644 --- a/examples/mario/src/main/java/de/unistuttgart/informatik/fius/icge/example/mario/Main.java +++ b/examples/mario/src/main/java/de/unistuttgart/informatik/fius/icge/example/mario/Main.java @@ -35,30 +35,32 @@ public static void main(String[] args) { prepareUiManager(sim.getUiManager()); sim.initialize(); - sim.getPlayfield().addEntity(new Position(1, 4), new Wall()); - sim.getPlayfield().addEntity(new Position(2, 4), new Wall()); - sim.getPlayfield().addEntity(new Position(3, 4), new Wall()); - sim.getPlayfield().addEntity(new Position(4, 4), new Wall()); + sim.getPlayfield().addEntity(new Position(-3, -1), new Wall()); + sim.getPlayfield().addEntity(new Position(-3, 0), new Wall()); + sim.getPlayfield().addEntity(new Position(-3, 1), new Wall()); + sim.getPlayfield().addEntity(new Position(3, -1), new Wall()); + sim.getPlayfield().addEntity(new Position(3, 0), new Wall()); + sim.getPlayfield().addEntity(new Position(3, 1), new Wall()); - Mario mario = new Mario(); + Mario walkingMario = new Mario(); - sim.getPlayfield().addEntity(new Position(0, 0), mario); + Mario spinningMario = new Mario(); + + sim.getPlayfield().addEntity(new Position(-1, 0), walkingMario); + sim.getPlayfield().addEntity(new Position(0, 0), spinningMario); + + String walkingProgramName = "Walking"; + + sim.getEntityProgramRegistry().registerEntityProgram(walkingProgramName, new WalkingProgram()); sim.getSimulationClock().start(); - mario.turnClockWise(); - mario.turnClockWise(); - mario.turnClockWise(); - mario.turnClockWise(); - mario.move(); - mario.turnClockWise(); + sim.getEntityProgramRunner().run(walkingProgramName, walkingMario); + + while (true) { + spinningMario.turnClockWise(); + } - mario.move(); - mario.move(); - mario.move(); - mario.moveIfPossible(); - System.out.println(mario.canMove()); - mario.move(); } private static void prepareUiManager(UiManager manager) { diff --git a/examples/mario/src/main/java/de/unistuttgart/informatik/fius/icge/example/mario/WalkingProgram.java b/examples/mario/src/main/java/de/unistuttgart/informatik/fius/icge/example/mario/WalkingProgram.java new file mode 100644 index 000000000..fa4aff71b --- /dev/null +++ b/examples/mario/src/main/java/de/unistuttgart/informatik/fius/icge/example/mario/WalkingProgram.java @@ -0,0 +1,35 @@ +/* + * 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.example.mario; + +import de.unistuttgart.informatik.fius.icge.example.mario.entity.Mario; +import de.unistuttgart.informatik.fius.icge.example.mario.entity.MarioProgram; + + +/** + * A program walking around mario a bit + * + * @author Tim Neumann + */ +public class WalkingProgram extends MarioProgram { + + @Override + public void run(final Mario mario) { + while (true) { + if (mario.canMove()) { + mario.move(); + } else { + mario.turnClockWise(); + mario.turnClockWise(); + } + } + } + +} diff --git a/examples/mario/src/main/java/de/unistuttgart/informatik/fius/icge/example/mario/entity/MarioProgram.java b/examples/mario/src/main/java/de/unistuttgart/informatik/fius/icge/example/mario/entity/MarioProgram.java new file mode 100644 index 000000000..1caf8bbc4 --- /dev/null +++ b/examples/mario/src/main/java/de/unistuttgart/informatik/fius/icge/example/mario/entity/MarioProgram.java @@ -0,0 +1,43 @@ +/* + * 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.example.mario.entity; + +import de.unistuttgart.informatik.fius.icge.simulation.entity.Entity; +import de.unistuttgart.informatik.fius.icge.simulation.entity.program.EntityProgram; + + +/** + * A program for mario + * + * @author Tim Neumann + */ +public abstract class MarioProgram implements EntityProgram { + + /** + * Run this mario program on the given mario + * + * @param mario + * The mario to run this program on + */ + public abstract void run(Mario mario); + + @Override + public void run(final Entity entity) { + if (entity instanceof Mario) { + this.run((Mario) entity); + } else throw new IllegalArgumentException("Cannot run on that entity."); + } + + @Override + public boolean canRunOn(final Entity entity) { + if (entity instanceof Mario) return true; + return false; + } +}