Final version
This commit is contained in:
parent
d4af46450c
commit
45774f8c59
|
@ -18,11 +18,11 @@ public class ElementListIterator implements Iterator<Element> {
|
||||||
public boolean hasNext() {
|
public boolean hasNext() {
|
||||||
if (position >= nodes.getLength()) return false;
|
if (position >= nodes.getLength()) return false;
|
||||||
|
|
||||||
// Check if there is a remaining element node
|
// Vérifie s'il reste un nœud élément
|
||||||
for (int i = position; i < nodes.getLength(); i++) {
|
for (int i = position; i < nodes.getLength(); i++) {
|
||||||
Node node = nodes.item(i);
|
Node node = nodes.item(i);
|
||||||
|
|
||||||
if (node.getNodeType() == Node.ELEMENT_NODE) return true;
|
if (isElementNode(node)) return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -35,7 +35,11 @@ public class ElementListIterator implements Iterator<Element> {
|
||||||
Node node = nodes.item(position);
|
Node node = nodes.item(position);
|
||||||
position++;
|
position++;
|
||||||
|
|
||||||
if (node.getNodeType() == Node.ELEMENT_NODE) return (Element) node;
|
if (isElementNode(node)) return (Element) node;
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean isElementNode(Node node) {
|
||||||
|
return node.getNodeType() == Node.ELEMENT_NODE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,12 @@ public abstract class Building {
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void processInput(Component input);
|
public abstract void processInput(Component input);
|
||||||
public abstract Optional<Component> update();
|
|
||||||
|
/**
|
||||||
|
* Continue la production.
|
||||||
|
* @return Le composant produit. Si le bâtiment n'a aucun produit fini ce tour ci, l'Optional retourné sera vide.
|
||||||
|
*/
|
||||||
|
public abstract Optional<Component> produce();
|
||||||
public abstract BuildingMetadata getMetadata();
|
public abstract BuildingMetadata getMetadata();
|
||||||
|
|
||||||
public int getId() {
|
public int getId() {
|
||||||
|
@ -41,8 +46,4 @@ public abstract class Building {
|
||||||
public BuildingState getState() {
|
public BuildingState getState() {
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setState(BuildingState state) {
|
|
||||||
this.state = state;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,63 +0,0 @@
|
||||||
package simulation;
|
|
||||||
|
|
||||||
public class ComponentRoute {
|
|
||||||
public static final int SPEED = 3;
|
|
||||||
|
|
||||||
private final Component component;
|
|
||||||
private final Building outputBuilding;
|
|
||||||
private final int directionX;
|
|
||||||
private final int directionY;
|
|
||||||
|
|
||||||
private int x;
|
|
||||||
private int y;
|
|
||||||
|
|
||||||
public ComponentRoute(Component component, Building outputBuilding, int x, int y) {
|
|
||||||
this.component = component;
|
|
||||||
this.outputBuilding = outputBuilding;
|
|
||||||
this.x = x;
|
|
||||||
this.y = y;
|
|
||||||
|
|
||||||
directionX = normalizedDirection(x, outputBuilding.getX());
|
|
||||||
directionY = normalizedDirection(y, outputBuilding.getY());
|
|
||||||
}
|
|
||||||
|
|
||||||
private int normalizedDirection(int from, int to) {
|
|
||||||
return Integer.compare(to, from) * SPEED;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updatePosition() {
|
|
||||||
x += directionX;
|
|
||||||
y += directionY;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sendToOutputBuilding() {
|
|
||||||
outputBuilding.processInput(component);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isTransitFinished() {
|
|
||||||
boolean xSame = directionX == 0 ||
|
|
||||||
(directionX > 0 && x >= outputBuilding.getX()) ||
|
|
||||||
(directionX < 0 && x <= outputBuilding.getX());
|
|
||||||
boolean ySame = directionY == 0 ||
|
|
||||||
(directionY > 0 && y >= outputBuilding.getY()) ||
|
|
||||||
(directionY < 0 && y <= outputBuilding.getY());
|
|
||||||
|
|
||||||
return xSame && ySame;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Component getComponent() {
|
|
||||||
return component;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Building getOutputBuilding() {
|
|
||||||
return outputBuilding;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getX() {
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getY() {
|
|
||||||
return y;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -12,31 +12,40 @@ public class Factory extends Building implements WarehouseObserver {
|
||||||
private final FactoryMetadata metadata;
|
private final FactoryMetadata metadata;
|
||||||
private final Map<ComponentType, Integer> inputsCount = new HashMap<>();
|
private final Map<ComponentType, Integer> inputsCount = new HashMap<>();
|
||||||
|
|
||||||
|
private float productionInterval;
|
||||||
private boolean isProductionStarted;
|
private boolean isProductionStarted;
|
||||||
private boolean warehouseFull;
|
private boolean haltProduction;
|
||||||
private long productionTicks = 0L;
|
private int productionTicks = 0;
|
||||||
|
|
||||||
public Factory(int id, String type, int x, int y, FactoryMetadata metadata) {
|
public Factory(int id, String type, int x, int y, FactoryMetadata metadata) {
|
||||||
super(id, type, x, y);
|
super(id, type, x, y);
|
||||||
this.metadata = metadata;
|
this.metadata = metadata;
|
||||||
|
this.productionInterval = metadata.getProductionInterval();
|
||||||
|
|
||||||
// Les usines sans entrées devraient toujours produire
|
// Les usines sans entrées doivent toujours produire
|
||||||
isProductionStarted = metadata.getInputs().size() == 0;
|
isProductionStarted = metadata.getInputs().size() == 0;
|
||||||
if (isProductionStarted) {
|
if (isProductionStarted) {
|
||||||
productionTicks = metadata.getProductionInterval();
|
productionTicks = (int) productionInterval;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<Component> update() {
|
public Optional<Component> produce() {
|
||||||
if (!isProductionStarted || (productionTicks == 0 && warehouseFull)) return Optional.empty();
|
if (!isProductionStarted ||
|
||||||
|
(productionTicks == 0 && haltProduction)) { // La production en cours ne doit pas s'arrêter, mais l'usine ne doit pas produire un nouveau composant
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
productionTicks++;
|
productionTicks++;
|
||||||
if (productionTicks < metadata.getProductionInterval()) {
|
if (productionTicks < productionInterval) {
|
||||||
|
// La production n'est pas finie
|
||||||
updateState();
|
updateState();
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// La production est finie
|
||||||
|
// Réinitialise la progression de la production et défini l'état de l'usine à vide.
|
||||||
|
// Retourne le composant produit
|
||||||
if (metadata.getInputs().size() > 0) {
|
if (metadata.getInputs().size() > 0) {
|
||||||
isProductionStarted = false;
|
isProductionStarted = false;
|
||||||
}
|
}
|
||||||
|
@ -50,10 +59,13 @@ public class Factory extends Building implements WarehouseObserver {
|
||||||
@Override
|
@Override
|
||||||
public void processInput(Component input) {
|
public void processInput(Component input) {
|
||||||
int inputCount = 0;
|
int inputCount = 0;
|
||||||
|
|
||||||
|
// S'assure qu'une entrée pour ce type de composant existe.
|
||||||
if (inputsCount.containsKey(input.getType())) {
|
if (inputsCount.containsKey(input.getType())) {
|
||||||
inputCount = inputsCount.get(input.getType());
|
inputCount = inputsCount.get(input.getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Augmente la quantité de ce type de composant en stock.
|
||||||
inputsCount.put(input.getType(), inputCount + 1);
|
inputsCount.put(input.getType(), inputCount + 1);
|
||||||
|
|
||||||
if (hasEnoughComponents()) {
|
if (hasEnoughComponents()) {
|
||||||
|
@ -67,12 +79,33 @@ public class Factory extends Building implements WarehouseObserver {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateWarehouseState(boolean full) {
|
public void updateWarehouseState(BuildingState state) {
|
||||||
warehouseFull = full;
|
updateProductionSpeed(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateProductionSpeed(BuildingState state) {
|
||||||
|
int originalProductionInterval = metadata.getProductionInterval();
|
||||||
|
|
||||||
|
// Augmente le temps de production selon l'état de l'entrepôt
|
||||||
|
switch (state) {
|
||||||
|
case EMPTY -> {
|
||||||
|
haltProduction = false;
|
||||||
|
productionInterval = originalProductionInterval;
|
||||||
|
}
|
||||||
|
case ONE_THIRD -> {
|
||||||
|
haltProduction = false;
|
||||||
|
productionInterval = originalProductionInterval * 1.33333f;
|
||||||
|
}
|
||||||
|
case TWO_THIRD -> {
|
||||||
|
haltProduction = false;
|
||||||
|
productionInterval = originalProductionInterval * 2.66666f;
|
||||||
|
}
|
||||||
|
default -> haltProduction = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateState() {
|
private void updateState() {
|
||||||
float productionRatio = productionTicks / (float) metadata.getProductionInterval();
|
float productionRatio = productionTicks / productionInterval;
|
||||||
if (productionRatio >= (2 / 3f)) {
|
if (productionRatio >= (2 / 3f)) {
|
||||||
state = BuildingState.FULL;
|
state = BuildingState.FULL;
|
||||||
} else if (productionRatio >= (1 / 3f)) {
|
} else if (productionRatio >= (1 / 3f)) {
|
||||||
|
@ -84,7 +117,8 @@ public class Factory extends Building implements WarehouseObserver {
|
||||||
|
|
||||||
private boolean hasEnoughComponents() {
|
private boolean hasEnoughComponents() {
|
||||||
for (FactoryInput input : metadata.getInputs()) {
|
for (FactoryInput input : metadata.getInputs()) {
|
||||||
if (!inputsCount.containsKey(input.getType()) || inputsCount.get(input.getType()) < input.getQuantity()) {
|
if (!inputsCount.containsKey(input.getType()) ||
|
||||||
|
inputsCount.get(input.getType()) < input.getQuantity()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,6 +127,7 @@ public class Factory extends Building implements WarehouseObserver {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startProduction() {
|
private void startProduction() {
|
||||||
|
// Retire les composants nécessaires des entrées
|
||||||
for (FactoryInput input : metadata.getInputs()) {
|
for (FactoryInput input : metadata.getInputs()) {
|
||||||
int inputCount = inputsCount.get(input.getType());
|
int inputCount = inputsCount.get(input.getType());
|
||||||
inputsCount.put(input.getType(), inputCount - input.getQuantity());
|
inputsCount.put(input.getType(), inputCount - input.getQuantity());
|
||||||
|
|
|
@ -3,7 +3,7 @@ package simulation;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
public class RandomSellStrategy implements SellStrategy {
|
public class RandomSellStrategy implements SellStrategy {
|
||||||
private static final int SELL_CHANCE_PERCENT = 1;
|
private static final int SELL_CHANCE_PERCENT = 1; // 1% de chance par tour
|
||||||
|
|
||||||
private final Random random = new Random();
|
private final Random random = new Random();
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
package simulation;
|
package simulation;
|
||||||
|
|
||||||
public interface SellStrategy {
|
public interface SellStrategy {
|
||||||
|
/**
|
||||||
|
* @return S'il est possible de vendre ce tour ci.
|
||||||
|
*/
|
||||||
boolean shouldSell();
|
boolean shouldSell();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
package simulation;
|
package simulation;
|
||||||
|
|
||||||
public class TimedSellStrategy implements SellStrategy {
|
public class TimedSellStrategy implements SellStrategy {
|
||||||
private static final int SELL_INTERVAL = 300;
|
private static final int SELL_INTERVAL = 1000;
|
||||||
|
|
||||||
private long tick = 0L;
|
private long turn = 0L;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean shouldSell() {
|
public boolean shouldSell() {
|
||||||
return ++tick % SELL_INTERVAL == 0;
|
return ++turn % SELL_INTERVAL == 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ public class Warehouse extends Building implements WarehouseSubject {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<Component> update() {
|
public Optional<Component> produce() {
|
||||||
if (planeCount > 0 && sellStrategy.shouldSell()) {
|
if (planeCount > 0 && sellStrategy.shouldSell()) {
|
||||||
sellPlane();
|
sellPlane();
|
||||||
}
|
}
|
||||||
|
@ -71,13 +71,12 @@ public class Warehouse extends Building implements WarehouseSubject {
|
||||||
observers.add(observer);
|
observers.add(observer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void detach(WarehouseObserver observer) {
|
public void dettach(WarehouseObserver observer) {
|
||||||
observers.remove(observer);
|
observers.remove(observer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void notifyObservers() {
|
public void notifyObservers() {
|
||||||
boolean isFull = planeCount >= capacity;
|
observers.forEach(o -> o.updateWarehouseState(state));
|
||||||
observers.forEach(o -> o.updateWarehouseState(isFull));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -4,7 +4,7 @@ public interface WarehouseObserver {
|
||||||
/**
|
/**
|
||||||
* Méthode appelée lorsque l'état d'un entrepot change.
|
* Méthode appelée lorsque l'état d'un entrepot change.
|
||||||
*
|
*
|
||||||
* @param full Si l'entrepot est plein ou non
|
* @param full Si l'entrepôt est plein ou non
|
||||||
*/
|
*/
|
||||||
void updateWarehouseState(boolean full);
|
void updateWarehouseState(BuildingState state);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,6 @@ package simulation;
|
||||||
|
|
||||||
public interface WarehouseSubject {
|
public interface WarehouseSubject {
|
||||||
void attach(WarehouseObserver observer);
|
void attach(WarehouseObserver observer);
|
||||||
void detach(WarehouseObserver observer);
|
void dettach(WarehouseObserver observer);
|
||||||
void notifyObservers();
|
void notifyObservers();
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import javax.swing.*;
|
||||||
|
|
||||||
public class Environment extends SwingWorker<Object, String> {
|
public class Environment extends SwingWorker<Object, String> {
|
||||||
private static final int DELAY = 100;
|
private static final int DELAY = 100;
|
||||||
public static final String TICK_PROPERTY_NAME = "tick";
|
public static final String TURN_PROPERTY_NAME = "turn";
|
||||||
|
|
||||||
private boolean active = true;
|
private boolean active = true;
|
||||||
private long tick = 0L;
|
private long tick = 0L;
|
||||||
|
@ -14,7 +14,7 @@ public class Environment extends SwingWorker<Object, String> {
|
||||||
while (active) {
|
while (active) {
|
||||||
Thread.sleep(DELAY);
|
Thread.sleep(DELAY);
|
||||||
|
|
||||||
firePropertyChange(TICK_PROPERTY_NAME, tick, ++tick);
|
firePropertyChange(TURN_PROPERTY_NAME, tick, ++tick);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,8 +33,11 @@ public class MainPanel extends JLayeredPane {
|
||||||
simulationData = configuration.getSimulationData();
|
simulationData = configuration.getSimulationData();
|
||||||
|
|
||||||
buildingsById.clear();
|
buildingsById.clear();
|
||||||
|
componentIcons.clear();
|
||||||
|
removeAll();
|
||||||
|
repaint();
|
||||||
|
|
||||||
int routesOffset = 0;
|
int routesOffset = 0; // Cet offset permet aux lignes de routes d'être au centre des icônes des bâtiments.
|
||||||
for (Building building : simulationData.getBuildings()) {
|
for (Building building : simulationData.getBuildings()) {
|
||||||
BuildingIcon icon = this.initializeBuilding(building);
|
BuildingIcon icon = this.initializeBuilding(building);
|
||||||
routesOffset = icon.getWidth() / 2;
|
routesOffset = icon.getWidth() / 2;
|
||||||
|
@ -72,27 +75,37 @@ public class MainPanel extends JLayeredPane {
|
||||||
if (configuration == null) return;
|
if (configuration == null) return;
|
||||||
|
|
||||||
updateBuildings();
|
updateBuildings();
|
||||||
|
updateComponentIcons();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateComponentIcons() {
|
||||||
Collection<ComponentIcon> removedIcons = new ArrayList<>();
|
Collection<ComponentIcon> removedIcons = new ArrayList<>();
|
||||||
|
|
||||||
for (ComponentIcon icon : componentIcons) {
|
componentIcons.forEach(i -> {
|
||||||
if (icon.isAtDestination()) {
|
i.updatePosition();
|
||||||
removedIcons.add(icon);
|
|
||||||
|
boolean destinationReached = i.isAtDestination();
|
||||||
|
if (destinationReached) {
|
||||||
|
removedIcons.add(i);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
removedIcons.forEach(i -> {
|
removedIcons.forEach(i -> {
|
||||||
componentIcons.remove(i);
|
componentIcons.remove(i);
|
||||||
remove(i);
|
remove(i);
|
||||||
|
|
||||||
Building outputBuilding = getBuildingById(i.getDestinationId());
|
pushComponentToBuilding(i.getComponent(), i.getDestinationId());
|
||||||
outputBuilding.processInput(i.getComponent());
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void pushComponentToBuilding(Component c, int buildingId) {
|
||||||
|
Building outputBuilding = getBuildingById(buildingId);
|
||||||
|
outputBuilding.processInput(c);
|
||||||
|
}
|
||||||
|
|
||||||
private void updateBuildings() {
|
private void updateBuildings() {
|
||||||
simulationData.getBuildings().forEach(b -> {
|
simulationData.getBuildings().forEach(b -> {
|
||||||
b.update().ifPresent(c -> addComponentRoute(c, b));
|
b.produce().ifPresent(c -> addComponentRoute(c, b));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ public class MainWindow extends JFrame implements PropertyChangeListener {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void propertyChange(PropertyChangeEvent event) {
|
public void propertyChange(PropertyChangeEvent event) {
|
||||||
if (event.getPropertyName().equals(Environment.TICK_PROPERTY_NAME)) {
|
if (event.getPropertyName().equals(Environment.TURN_PROPERTY_NAME)) {
|
||||||
repaint();
|
repaint();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ import java.util.Map;
|
||||||
|
|
||||||
public class ComponentIcon extends JComponent {
|
public class ComponentIcon extends JComponent {
|
||||||
private static final Map<ComponentType, Image> loadedImages = new HashMap<>();
|
private static final Map<ComponentType, Image> loadedImages = new HashMap<>();
|
||||||
private static final int SPEED = 2;
|
private static final int SPEED = 1;
|
||||||
|
|
||||||
private final Component component;
|
private final Component component;
|
||||||
private final int destinationId;
|
private final int destinationId;
|
||||||
|
@ -37,12 +37,14 @@ public class ComponentIcon extends JComponent {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void paintComponent(Graphics g) {
|
public void paintComponent(Graphics g) {
|
||||||
position.translate(direction.x, direction.y);
|
|
||||||
|
|
||||||
g.translate(position.x, position.y);
|
g.translate(position.x, position.y);
|
||||||
g.drawImage(image, 0, 0, null);
|
g.drawImage(image, 0, 0, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updatePosition() {
|
||||||
|
position.translate(direction.x, direction.y);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isAtDestination() {
|
public boolean isAtDestination() {
|
||||||
return position.equals(destination);
|
return position.equals(destination);
|
||||||
}
|
}
|
||||||
|
@ -63,6 +65,8 @@ public class ComponentIcon extends JComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int normalizedDirection(int from, int to) {
|
private static int normalizedDirection(int from, int to) {
|
||||||
|
// compare retourne 1, 0 ou -1 en comparant to à from.
|
||||||
|
// Il suffit de multiplier ce résultat par la vitesse désirée pour trouver la direction normalisée.
|
||||||
return Integer.compare(to, from) * SPEED;
|
return Integer.compare(to, from) * SPEED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,9 @@ public class RouteLine extends JComponent {
|
||||||
int offsetFromOriginX = Math.min(from.x, to.x);
|
int offsetFromOriginX = Math.min(from.x, to.x);
|
||||||
int offsetFromOriginY = Math.min(from.y, to.y);
|
int offsetFromOriginY = Math.min(from.y, to.y);
|
||||||
|
|
||||||
|
// La position de la ligne dépend de x et y des bounds du JComponent.
|
||||||
|
// Ainsi, la position [0 0] est en fait [x y] des bounds.
|
||||||
|
// Il faut donc retirer ces positions pour ne pas dessiner en dehors du rectangle de dessin.
|
||||||
from.translate(-offsetFromOriginX, -offsetFromOriginY);
|
from.translate(-offsetFromOriginX, -offsetFromOriginY);
|
||||||
to.translate(-offsetFromOriginX, -offsetFromOriginY);
|
to.translate(-offsetFromOriginX, -offsetFromOriginY);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue