diff --git a/src/configuration/ElementListIterator.java b/src/configuration/ElementListIterator.java index bf61e06..ac9a8d7 100644 --- a/src/configuration/ElementListIterator.java +++ b/src/configuration/ElementListIterator.java @@ -18,11 +18,11 @@ public class ElementListIterator implements Iterator { public boolean hasNext() { 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++) { Node node = nodes.item(i); - if (node.getNodeType() == Node.ELEMENT_NODE) return true; + if (isElementNode(node)) return true; } return false; @@ -35,7 +35,11 @@ public class ElementListIterator implements Iterator { Node node = nodes.item(position); position++; - if (node.getNodeType() == Node.ELEMENT_NODE) return (Element) node; + if (isElementNode(node)) return (Element) node; return next(); } + + private static boolean isElementNode(Node node) { + return node.getNodeType() == Node.ELEMENT_NODE; + } } diff --git a/src/simulation/Building.java b/src/simulation/Building.java index fb5767e..3c9d6e7 100644 --- a/src/simulation/Building.java +++ b/src/simulation/Building.java @@ -19,7 +19,12 @@ public abstract class Building { } public abstract void processInput(Component input); - public abstract Optional 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 produce(); public abstract BuildingMetadata getMetadata(); public int getId() { @@ -41,8 +46,4 @@ public abstract class Building { public BuildingState getState() { return state; } - - public void setState(BuildingState state) { - this.state = state; - } } diff --git a/src/simulation/ComponentRoute.java b/src/simulation/ComponentRoute.java deleted file mode 100644 index 6a7dca2..0000000 --- a/src/simulation/ComponentRoute.java +++ /dev/null @@ -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; - } -} diff --git a/src/simulation/Factory.java b/src/simulation/Factory.java index 6b53487..e3ad99d 100644 --- a/src/simulation/Factory.java +++ b/src/simulation/Factory.java @@ -12,31 +12,40 @@ public class Factory extends Building implements WarehouseObserver { private final FactoryMetadata metadata; private final Map inputsCount = new HashMap<>(); + private float productionInterval; private boolean isProductionStarted; - private boolean warehouseFull; - private long productionTicks = 0L; + private boolean haltProduction; + private int productionTicks = 0; public Factory(int id, String type, int x, int y, FactoryMetadata metadata) { super(id, type, x, y); 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; if (isProductionStarted) { - productionTicks = metadata.getProductionInterval(); + productionTicks = (int) productionInterval; } } @Override - public Optional update() { - if (!isProductionStarted || (productionTicks == 0 && warehouseFull)) return Optional.empty(); + public Optional produce() { + 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++; - if (productionTicks < metadata.getProductionInterval()) { + if (productionTicks < productionInterval) { + // La production n'est pas finie updateState(); 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) { isProductionStarted = false; } @@ -50,10 +59,13 @@ public class Factory extends Building implements WarehouseObserver { @Override public void processInput(Component input) { int inputCount = 0; + + // S'assure qu'une entrée pour ce type de composant existe. if (inputsCount.containsKey(input.getType())) { inputCount = inputsCount.get(input.getType()); } + // Augmente la quantité de ce type de composant en stock. inputsCount.put(input.getType(), inputCount + 1); if (hasEnoughComponents()) { @@ -67,12 +79,33 @@ public class Factory extends Building implements WarehouseObserver { } @Override - public void updateWarehouseState(boolean full) { - warehouseFull = full; + public void updateWarehouseState(BuildingState state) { + 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() { - float productionRatio = productionTicks / (float) metadata.getProductionInterval(); + float productionRatio = productionTicks / productionInterval; if (productionRatio >= (2 / 3f)) { state = BuildingState.FULL; } else if (productionRatio >= (1 / 3f)) { @@ -84,7 +117,8 @@ public class Factory extends Building implements WarehouseObserver { private boolean hasEnoughComponents() { 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; } } @@ -93,6 +127,7 @@ public class Factory extends Building implements WarehouseObserver { } private void startProduction() { + // Retire les composants nécessaires des entrées for (FactoryInput input : metadata.getInputs()) { int inputCount = inputsCount.get(input.getType()); inputsCount.put(input.getType(), inputCount - input.getQuantity()); diff --git a/src/simulation/RandomSellStrategy.java b/src/simulation/RandomSellStrategy.java index 25a980a..086842e 100644 --- a/src/simulation/RandomSellStrategy.java +++ b/src/simulation/RandomSellStrategy.java @@ -3,7 +3,7 @@ package simulation; import java.util.Random; 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(); diff --git a/src/simulation/SellStrategy.java b/src/simulation/SellStrategy.java index 4432f1d..68ffc0f 100644 --- a/src/simulation/SellStrategy.java +++ b/src/simulation/SellStrategy.java @@ -1,5 +1,8 @@ package simulation; public interface SellStrategy { + /** + * @return S'il est possible de vendre ce tour ci. + */ boolean shouldSell(); } diff --git a/src/simulation/TimedSellStrategy.java b/src/simulation/TimedSellStrategy.java index 5ba25d9..fb71bd3 100644 --- a/src/simulation/TimedSellStrategy.java +++ b/src/simulation/TimedSellStrategy.java @@ -1,12 +1,12 @@ package simulation; 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 public boolean shouldSell() { - return ++tick % SELL_INTERVAL == 0; + return ++turn % SELL_INTERVAL == 0; } } diff --git a/src/simulation/Warehouse.java b/src/simulation/Warehouse.java index 3b0cd3b..a55cfe2 100644 --- a/src/simulation/Warehouse.java +++ b/src/simulation/Warehouse.java @@ -23,7 +23,7 @@ public class Warehouse extends Building implements WarehouseSubject { } @Override - public Optional update() { + public Optional produce() { if (planeCount > 0 && sellStrategy.shouldSell()) { sellPlane(); } @@ -71,13 +71,12 @@ public class Warehouse extends Building implements WarehouseSubject { observers.add(observer); } - public void detach(WarehouseObserver observer) { + public void dettach(WarehouseObserver observer) { observers.remove(observer); } public void notifyObservers() { - boolean isFull = planeCount >= capacity; - observers.forEach(o -> o.updateWarehouseState(isFull)); + observers.forEach(o -> o.updateWarehouseState(state)); } @Override diff --git a/src/simulation/WarehouseObserver.java b/src/simulation/WarehouseObserver.java index 3fb55ef..b94db58 100644 --- a/src/simulation/WarehouseObserver.java +++ b/src/simulation/WarehouseObserver.java @@ -4,7 +4,7 @@ public interface WarehouseObserver { /** * 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); } diff --git a/src/simulation/WarehouseSubject.java b/src/simulation/WarehouseSubject.java index 9c25750..2e2c09b 100644 --- a/src/simulation/WarehouseSubject.java +++ b/src/simulation/WarehouseSubject.java @@ -2,6 +2,6 @@ package simulation; public interface WarehouseSubject { void attach(WarehouseObserver observer); - void detach(WarehouseObserver observer); + void dettach(WarehouseObserver observer); void notifyObservers(); } diff --git a/src/view/Environment.java b/src/view/Environment.java index 223b989..aaded29 100644 --- a/src/view/Environment.java +++ b/src/view/Environment.java @@ -4,7 +4,7 @@ import javax.swing.*; public class Environment extends SwingWorker { 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 long tick = 0L; @@ -14,7 +14,7 @@ public class Environment extends SwingWorker { while (active) { Thread.sleep(DELAY); - firePropertyChange(TICK_PROPERTY_NAME, tick, ++tick); + firePropertyChange(TURN_PROPERTY_NAME, tick, ++tick); } return null; } diff --git a/src/view/MainPanel.java b/src/view/MainPanel.java index f2f2439..c58754c 100644 --- a/src/view/MainPanel.java +++ b/src/view/MainPanel.java @@ -33,8 +33,11 @@ public class MainPanel extends JLayeredPane { simulationData = configuration.getSimulationData(); 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()) { BuildingIcon icon = this.initializeBuilding(building); routesOffset = icon.getWidth() / 2; @@ -72,27 +75,37 @@ public class MainPanel extends JLayeredPane { if (configuration == null) return; updateBuildings(); + updateComponentIcons(); + } + private void updateComponentIcons() { Collection removedIcons = new ArrayList<>(); - for (ComponentIcon icon : componentIcons) { - if (icon.isAtDestination()) { - removedIcons.add(icon); + componentIcons.forEach(i -> { + i.updatePosition(); + + boolean destinationReached = i.isAtDestination(); + if (destinationReached) { + removedIcons.add(i); } - } + }); removedIcons.forEach(i -> { componentIcons.remove(i); remove(i); - Building outputBuilding = getBuildingById(i.getDestinationId()); - outputBuilding.processInput(i.getComponent()); + pushComponentToBuilding(i.getComponent(), i.getDestinationId()); }); } + private void pushComponentToBuilding(Component c, int buildingId) { + Building outputBuilding = getBuildingById(buildingId); + outputBuilding.processInput(c); + } + private void updateBuildings() { simulationData.getBuildings().forEach(b -> { - b.update().ifPresent(c -> addComponentRoute(c, b)); + b.produce().ifPresent(c -> addComponentRoute(c, b)); }); } diff --git a/src/view/MainWindow.java b/src/view/MainWindow.java index 8448d86..6cb5bb9 100644 --- a/src/view/MainWindow.java +++ b/src/view/MainWindow.java @@ -36,7 +36,7 @@ public class MainWindow extends JFrame implements PropertyChangeListener { @Override public void propertyChange(PropertyChangeEvent event) { - if (event.getPropertyName().equals(Environment.TICK_PROPERTY_NAME)) { + if (event.getPropertyName().equals(Environment.TURN_PROPERTY_NAME)) { repaint(); } } diff --git a/src/view/components/ComponentIcon.java b/src/view/components/ComponentIcon.java index 48e5a21..ffe5d4f 100644 --- a/src/view/components/ComponentIcon.java +++ b/src/view/components/ComponentIcon.java @@ -14,7 +14,7 @@ import java.util.Map; public class ComponentIcon extends JComponent { private static final Map loadedImages = new HashMap<>(); - private static final int SPEED = 2; + private static final int SPEED = 1; private final Component component; private final int destinationId; @@ -37,12 +37,14 @@ public class ComponentIcon extends JComponent { @Override public void paintComponent(Graphics g) { - position.translate(direction.x, direction.y); - g.translate(position.x, position.y); g.drawImage(image, 0, 0, null); } + public void updatePosition() { + position.translate(direction.x, direction.y); + } + public boolean isAtDestination() { return position.equals(destination); } @@ -63,6 +65,8 @@ public class ComponentIcon extends JComponent { } 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; } diff --git a/src/view/components/RouteLine.java b/src/view/components/RouteLine.java index 0ca54b4..75b92ed 100644 --- a/src/view/components/RouteLine.java +++ b/src/view/components/RouteLine.java @@ -18,6 +18,9 @@ public class RouteLine extends JComponent { int offsetFromOriginX = Math.min(from.x, to.x); 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); to.translate(-offsetFromOriginX, -offsetFromOriginY); }