All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.adrodoc55.minecraft.mpl.placement.MplProgramPlacer Maven / Gradle / Ivy

/*
 * Minecraft Programming Language (MPL): A language for easy development of command block
 * applications including an IDE.
 *
 * © Copyright (C) 2016 Adrodoc55
 *
 * This file is part of MPL.
 *
 * MPL is free software: you can redistribute it and/or modify it under the terms of the GNU General
 * Public License as published by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * MPL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
 * Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with MPL. If not, see
 * .
 *
 *
 *
 * Minecraft Programming Language (MPL): Eine Sprache für die einfache Entwicklung von Commandoblock
 * Anwendungen, inklusive einer IDE.
 *
 * © Copyright (C) 2016 Adrodoc55
 *
 * Diese Datei ist Teil von MPL.
 *
 * MPL ist freie Software: Sie können diese unter den Bedingungen der GNU General Public License,
 * wie von der Free Software Foundation, Version 3 der Lizenz oder (nach Ihrer Wahl) jeder späteren
 * veröffentlichten Version, weiterverbreiten und/oder modifizieren.
 *
 * MPL wird in der Hoffnung, dass es nützlich sein wird, aber OHNE JEDE GEWÄHRLEISTUNG,
 * bereitgestellt; sogar ohne die implizite Gewährleistung der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN
 * BESTIMMTEN ZWECK. Siehe die GNU General Public License für weitere Details.
 *
 * Sie sollten eine Kopie der GNU General Public License zusammen mit MPL erhalten haben. Wenn
 * nicht, siehe .
 */
package de.adrodoc55.minecraft.mpl.placement;

import static de.adrodoc55.minecraft.mpl.compilation.CompilerOptions.CompilerOption.DELETE_ON_UNINSTALL;
import static de.kussm.direction.Direction.EAST;
import static de.kussm.direction.Directions.$;
import static java.util.Comparator.naturalOrder;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Set;

import de.adrodoc55.minecraft.coordinate.Coordinate3D;
import de.adrodoc55.minecraft.coordinate.Direction3D;
import de.adrodoc55.minecraft.coordinate.Orientation3D;
import de.adrodoc55.minecraft.mpl.chain.ChainContainer;
import de.adrodoc55.minecraft.mpl.chain.CommandBlockChain;
import de.adrodoc55.minecraft.mpl.chain.CommandChain;
import de.adrodoc55.minecraft.mpl.commands.chainlinks.Command;
import de.adrodoc55.minecraft.mpl.compilation.CompilerOptions;
import de.kussm.chain.Chain;
import de.kussm.chain.ChainLinkType;
import de.kussm.direction.Directions;
import de.kussm.position.Position;

/**
 * @author Adrodoc55
 */
public class MplProgramPlacer extends MplChainPlacer {

  private final int[] occupied;
  private Position size;

  public MplProgramPlacer(ChainContainer container, CompilerOptions options) {
    super(container, options);
    int size = container.getChains().size() + 2;
    this.occupied = new int[size];
  }

  @Override
  public List place() throws NotEnoughSpaceException {
    List chains = new ArrayList<>(container.getChains());
    // start with the longest chain
    chains.sort((o1, o2) -> {
      return Integer.compare(o1.getCommands().size(), o2.getCommands().size()) * -1;
    });
    while (true) {
      this.chains.clear();
      try {
        for (CommandChain chain : chains) {
          addChain(chain);
        }
        addUnInstallation();
        break;
      } catch (NotEnoughSpaceException ex) {
        increaseSize();
        continue;
      }
    }
    return this.chains;
  }

  private void addChain(CommandChain chain) throws NotEnoughSpaceException {
    Coordinate3D start = findStart(chain);
    CommandBlockChain materialized = generateFlat(chain, start, newTemplate(getSize().getX()));
    occupyBlocks(materialized);
    chains.add(materialized);
  }

  private void increaseSize() throws NotEnoughSpaceException {
    Coordinate3D max = container.getMax();
    Orientation3D o = getOrientation();
    Direction3D a = o.getA();
    Direction3D b = o.getB();
    int maxA = max.get(a.getAxis());
    int maxB = max.get(b.getAxis());
    int x = getSize().getX();
    int y = getSize().getY();
    if (x < maxA || maxA < 0) {
      size = Position.at(x + 1, y);
    } else if (y < maxB || maxB < 0) {
      size = Position.at(x, y + 1);
    } else {
      throw new NotEnoughSpaceException();
    }
  }

  /**
   * Returns the current size to place the program. This is an exclusive, zero based
   * {@link Position}, so (1, 1) refers to a 1 block size.
   *
   * @return the current size to place the program
   * @throws NotEnoughSpaceException if the maximum coordinate is to small to place the entire
   *         program
   */
  protected Position getSize() throws NotEnoughSpaceException {
    if (size == null) {
      size = calculateInitialSize();
    }
    return size;
  }

  private Position calculateInitialSize() throws NotEnoughSpaceException {
    Orientation3D orientation = getOrientation();
    Direction3D a = orientation.getA();
    Direction3D b = orientation.getB();
    int maxA = container.getMax().get(a.getAxis());
    int maxB = container.getMax().get(b.getAxis());
    if (maxA >= 0 && maxB >= 0) {
      return Position.at(maxA, maxB);
    }
    if (maxB >= 0) {
      int minA = estimateMinA();
      return Position.at(minA, maxB);
    }
    int minA;
    if (maxA >= 0) {
      minA = maxA;
    } else {
      minA = estimateMinA();
    }

    int minB = 0;
    while (true) {
      int currentA = minA;
      for (CommandChain c : container.getChains()) {
        minB = Math.max(minB, getMinB(c, newTemplate(currentA)));
      }
      minB = Math.max(maxB, getMinUnInstallB());
      if (minA < minB) {
        minA++;
        continue;
      }
      return Position.at(minA, minB);
    }
  }

  private int getMinUnInstallB() {
    return (int) Math.ceil(Math.sqrt(getUnInstallLength()));
  }

  /**
   * Estimates the minimal length in the a direction required to place all chains efficiently.
   *
   * @return the minimal length in the a direction
   */
  private int estimateMinA() {
    int unInstallLength = getUnInstallLength();

    int longestProcessLength = container.getChains().stream()//
        .map(chain -> chain.getCommands().size())//
        .max(naturalOrder()).orElse(0);

    int longestChainLength = Math.max(unInstallLength, longestProcessLength);

    int sqrtLength = (int) Math.ceil(Math.sqrt(longestChainLength));
    return Math.max(sqrtLength, getLongestSuccessiveConditionalCount() + 3);
  }

  private int getUnInstallLength() {
    int installLength = 3 + container.getChains().size()//
        + container.getInstall().getCommands().size()//
        + container.getUninstall().getCommands().size();
    return installLength;
  }

  /**
   * Finds the next free starting coordinate for this chain. This method takes all chains that have
   * been added to {@link #chains} into account. Therefore the starting coordinate is guaranteed to
   * allow a valid placement of the chain without leaving the boundaries of {@link #getSize()}.
   *
   * @param chain the {@link CommandChain} to search a start for
   * @return a free coordinate to start the chain placement at
   * @throws NotEnoughSpaceException if the current size is to small to place the chain
   */
  protected Coordinate3D findStart(CommandChain chain) throws NotEnoughSpaceException {
    Position size = getSize();
    int minB = getMinB(chain, newTemplate(size.getX()));
    while (true) {
      Coordinate3D start = findStart(minB);
      LinkedHashMap placed = place(chain, start, newTemplate(size.getX()));
      int startB = start.get(getOrientation().getB());
      int actualB = getMaxY(placed.keySet());
      minB = startB + actualB;
      if (minB <= size.getY()) {
        return start;
      }
    }
  }

  /**
   * Finds the next free starting coordinate for a chain with the given height in the b direction.
   *
   * @param b the height of the chain
   * @return a free coordinate to start the chain placement at
   * @throws NotEnoughSpaceException if the current size is to small to place the chain
   */
  protected Coordinate3D findStart(int b) throws NotEnoughSpaceException {
    Orientation3D orientation = getOrientation();
    Direction3D cDir = orientation.getC();
    Coordinate3D bPos = orientation.getB().toCoordinate();
    Coordinate3D cPos = cDir.toCoordinate();

    Position size = getSize();
    for (int c = 0; c < occupied.length; c++) {
      int totalB = occupied[c] + b;
      if (totalB <= size.getY()) {
        return cPos.mult(c).plus(bPos.mult(occupied[c]));
      }
    }
    throw new NotEnoughSpaceException();
  }

  /**
   * Returns the minimal height in the b direction needed to place the chain along the template
   * disregarding forbidden transmitter and receiver positions.
   *
   * @param chain the {@link CommandChain} to place
   * @param template along which the chain will be placed
   * @return the minimal height in the b direction
   * @throws NotEnoughSpaceException if the template is too small
   */
  protected int getMinB(CommandChain chain, Directions template) throws NotEnoughSpaceException {
    Chain chainLinkChain = toChainLinkChain(chain.getCommands());
    LinkedHashMap placed = place(chainLinkChain, template);
    Set keySet = placed.keySet();
    return getMaxY(keySet) + 1; // +1 because y is zero based, but we want the actual height
  }

  /**
   * Returns the greatest y value of the given {@link Position}s.
   *
   * @param positions {@link Collection} of {@link Position}s
   * @return the greatest y value
   */
  protected int getMaxY(Collection positions) {
    return positions.stream().map(p -> p.getY()).max(naturalOrder()).orElse(0);
  }

  protected void occupyBlocks(CommandBlockChain materialized) {
    Orientation3D orientation = getOrientation();
    Direction3D b = orientation.getB();
    Direction3D c = orientation.getC();
    Coordinate3D max = materialized.getBoundaries(orientation);
    int maxB = max.get(b.getAxis());
    int maxC = max.get(c.getAxis());
    occupied[Math.abs(maxC)] = Math.abs(maxB) + 1;
  }

  private void addUnInstallation() throws NotEnoughSpaceException {
    // move all chains by 1 block, if installation or uninstallation is added.
    // if there is at least one process, both installation and unistallation will be added.
    if (!container.getChains().isEmpty()//
        || !getInstall().getCommands().isEmpty()//
        || !getUninstall().getCommands().isEmpty()) {
      for (CommandBlockChain chain : chains) {
        chain.move(getOrientation().getC().toCoordinate());
      }
    }
    generateUnInstall();
  }

  /**
   * This method generates both installation uninstallation of the program at (0, 0, 0) and adds
   * them to {@link #chains}. The resulting chains are in the same flat plane.
   *
   * @throws NotEnoughSpaceException if the current size is to small to generate install or
   *         uninstall
   * @see #generateFlat(CommandChain, Coordinate3D, Directions)
   */
  protected void generateUnInstall() throws NotEnoughSpaceException {
    CommandChain uninstall = getPopulatedUninstall();
    Command deleteOnUninstall = null;
    if (options.hasOption(DELETE_ON_UNINSTALL)) {
      deleteOnUninstall = new Command();
      uninstall.getCommands().add(deleteOnUninstall);
    }

    if (!uninstall.getCommands().isEmpty()) {
      Coordinate3D start = getOrientation().getB().toCoordinate();
      Directions template = newUninstallTemplate();
      CommandBlockChain generated = generateFlat(uninstall, start, template);
      chains.add(generated);
    }

    CommandChain install = getPopulatedInstall();
    if (!install.getCommands().isEmpty()) {
      Coordinate3D start = new Coordinate3D();
      Directions template = newInstallTemplate();
      CommandBlockChain generated = generateFlat(install, start, template);
      chains.add(generated);
    }

    if (deleteOnUninstall != null) {
      deleteOnUninstall.setCommand(getDeleteCommand());
    }
  }

  private Directions newInstallTemplate() throws NotEnoughSpaceException {
    return $(EAST.repeat(Math.abs(getUninstallA())), newTemplate(getInstallA()));
  }

  private Directions newUninstallTemplate() throws NotEnoughSpaceException {
    return newTemplate(getUninstallA());
  }

  private int getInstallA() throws NotEnoughSpaceException {
    // Bei ungerader Größe hat install einen Block weniger, da install effektiv 2 Reihen mehr hat.
    return getSize().getX() / 2;
  }

  private int getUninstallA() throws NotEnoughSpaceException {
    return getSize().getX() - getInstallA();
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy