Final version
This commit is contained in:
parent
d4af46450c
commit
45774f8c59
|
@ -18,11 +18,11 @@ public class ElementListIterator implements Iterator<Element> {
|
|||
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<Element> {
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,12 @@ public abstract class Building {
|
|||
}
|
||||
|
||||
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 int getId() {
|
||||
|
@ -41,8 +46,4 @@ public abstract class Building {
|
|||
public BuildingState getState() {
|
||||
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 Map<ComponentType, Integer> 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<Component> update() {
|
||||
if (!isProductionStarted || (productionTicks == 0 && warehouseFull)) return Optional.empty();
|
||||
public Optional<Component> 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());
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
package simulation;
|
||||
|
||||
public interface SellStrategy {
|
||||
/**
|
||||
* @return S'il est possible de vendre ce tour ci.
|
||||
*/
|
||||
boolean shouldSell();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ public class Warehouse extends Building implements WarehouseSubject {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Optional<Component> update() {
|
||||
public Optional<Component> 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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,6 @@ package simulation;
|
|||
|
||||
public interface WarehouseSubject {
|
||||
void attach(WarehouseObserver observer);
|
||||
void detach(WarehouseObserver observer);
|
||||
void dettach(WarehouseObserver observer);
|
||||
void notifyObservers();
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import javax.swing.*;
|
|||
|
||||
public class Environment extends SwingWorker<Object, String> {
|
||||
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<Object, String> {
|
|||
while (active) {
|
||||
Thread.sleep(DELAY);
|
||||
|
||||
firePropertyChange(TICK_PROPERTY_NAME, tick, ++tick);
|
||||
firePropertyChange(TURN_PROPERTY_NAME, tick, ++tick);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -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<ComponentIcon> 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));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ import java.util.Map;
|
|||
|
||||
public class ComponentIcon extends JComponent {
|
||||
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 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue