/*******************************************************************************
* Copyright (c) 2015 - 2017
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*******************************************************************************/
package jsettlers.logic.map.grid.partition.manager.materials.requests;
import jsettlers.common.buildings.EBuildingType;
import jsettlers.common.map.partition.IMaterialsDistributionSettings;
import jsettlers.common.material.EPriority;
import jsettlers.common.position.ShortPoint2D;
import jsettlers.common.utils.collections.list.DoubleLinkedList;
import jsettlers.logic.constants.MatchConstants;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.Arrays;
import java.util.Iterator;
/**
* This class is an advanced priority queue for material requests. The requests are served according to the settings. The settings specify the
* probability that a given type of building will be served.
*
* @author Andreas Eberle
*
*/
public final class MaterialsForBuildingsRequestPriorityQueue extends AbstractMaterialRequestPriorityQueue {
private static final long serialVersionUID = 4856036773080549412L;
private static final EBuildingType[] ADDITIONAL_BUILDING_TYPES = {EBuildingType.STOCK, EBuildingType.HARBOR, EBuildingType.MARKET_PLACE};
private static final int NUMBER_OF_ADDITIONAL_BUILDING_TYPES = ADDITIONAL_BUILDING_TYPES.length;
private final DoubleLinkedList<MaterialRequestObject> queues[][];
private final IMaterialsDistributionSettings settings;
private transient int[] buildingTypesToIndex;
@SuppressWarnings("unchecked")
public MaterialsForBuildingsRequestPriorityQueue(IMaterialsDistributionSettings settings) {
this.settings = settings;
queues = new DoubleLinkedList[EPriority.NUMBER_OF_PRIORITIES][];
for (int i = 0; i < queues.length; i++) {
queues[i] = DoubleLinkedList.getArray(settings.getNumberOfBuildingTypes() + NUMBER_OF_ADDITIONAL_BUILDING_TYPES);
}
calculateBuildingTypesToIndex();
}
private void calculateBuildingTypesToIndex() {
buildingTypesToIndex = new int[EBuildingType.NUMBER_OF_BUILDINGS];
for (int i = 0; i < buildingTypesToIndex.length; i++) {
buildingTypesToIndex[i] = -1;
}
int numberOfBuildings = settings.getNumberOfBuildingTypes();
for (int i = 0; i < numberOfBuildings; i++) {
buildingTypesToIndex[settings.getBuildingType(i).ordinal] = i;
}
for (int i = 0; i < NUMBER_OF_ADDITIONAL_BUILDING_TYPES; i++) {
buildingTypesToIndex[ADDITIONAL_BUILDING_TYPES[i].ordinal] = numberOfBuildings + i;
}
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ois.defaultReadObject();
calculateBuildingTypesToIndex();
}
@Override
protected DoubleLinkedList<MaterialRequestObject> getQueue(EPriority priority, EBuildingType buildingType) {
int buildingIndex = buildingTypesToIndex[buildingType.ordinal];
assert buildingIndex >= 0 : "Unknown building for this queue: " + buildingType;
return queues[priority.ordinal][buildingIndex];
}
@Override
protected MaterialRequestObject getRequestForPriority(int priority) {
DoubleLinkedList<MaterialRequestObject>[] queues = this.queues[priority];
int startIndex = getRandomStartIndex();
final int numberOfBuildingTypes = settings.getNumberOfBuildingTypes();
final int numberOfBuildings = numberOfBuildingTypes + NUMBER_OF_ADDITIONAL_BUILDING_TYPES;
for (int i = 0; i < numberOfBuildings; i++) {
int buildingIdx = (i + startIndex) % numberOfBuildings;
// if this building type should not receive any materials, skip it; if it is additional building, always check it
if (buildingIdx < numberOfBuildingTypes && settings.getProbablity(buildingIdx) <= 0.0f) { continue; }
MaterialRequestObject foundRequest = findRequestInQueue(queues[buildingIdx]);
if (foundRequest != null) {
return foundRequest;
}
}
return null;
}
private int getRandomStartIndex() {
float randomNumber = MatchConstants.random().nextFloat();
float sum = 0;
int numberOfBuildings = settings.getNumberOfBuildingTypes();
for (int i = 0; i < numberOfBuildings; i++) {
sum += settings.getProbablity(i);
if (randomNumber < sum) {
return i;
}
}
System.err.println("ERROR: No correct material distribution!");
return 0;
}
@Override
public void moveObjectsOfPositionTo(ShortPoint2D position, AbstractMaterialRequestPriorityQueue newAbstractQueue) {
assert newAbstractQueue instanceof MaterialsForBuildingsRequestPriorityQueue : "can't move positions between diffrent types of queues.";
MaterialsForBuildingsRequestPriorityQueue newQueue = (MaterialsForBuildingsRequestPriorityQueue) newAbstractQueue;
final int numberOfBuildings = settings.getNumberOfBuildingTypes();
for (int priorityIndex = 0; priorityIndex < queues.length; priorityIndex++) {
DoubleLinkedList<MaterialRequestObject>[] priorityQueue = queues[priorityIndex];
for (int queueIdx = 0; queueIdx < numberOfBuildings; queueIdx++) {
Iterator<MaterialRequestObject> iterator = priorityQueue[queueIdx].iterator();
while (iterator.hasNext()) {
MaterialRequestObject curr = iterator.next();
if (curr.getPos().equals(position)) {
iterator.remove();
newQueue.queues[priorityIndex][queueIdx].pushEnd(curr);
curr.requestQueue = newQueue;
}
}
}
}
}
@Override
public void mergeInto(AbstractMaterialRequestPriorityQueue newAbstractQueue) {
assert newAbstractQueue instanceof MaterialsForBuildingsRequestPriorityQueue : "can't move positions between diffrent types of queues.";
MaterialsForBuildingsRequestPriorityQueue newQueue = (MaterialsForBuildingsRequestPriorityQueue) newAbstractQueue;
final int numberOfBuildings = settings.getNumberOfBuildingTypes();
for (int priorityIndex = 0; priorityIndex < queues.length; priorityIndex++) {
for (int queueIdx = 0; queueIdx < numberOfBuildings; queueIdx++) {
DoubleLinkedList<MaterialRequestObject> currList = queues[priorityIndex][queueIdx];
DoubleLinkedList<MaterialRequestObject> newList = newQueue.queues[priorityIndex][queueIdx];
for (MaterialRequestObject request : currList) {
request.requestQueue = newQueue;
}
currList.mergeInto(newList);
}
}
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Arrays.hashCode(buildingTypesToIndex);
result = prime * result + Arrays.hashCode(queues);
result = prime * result + ((settings == null) ? 0 : settings.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) { return true; }
if (obj == null) { return false; }
if (getClass() != obj.getClass()) { return false; }
MaterialsForBuildingsRequestPriorityQueue other = (MaterialsForBuildingsRequestPriorityQueue) obj;
if (!Arrays.equals(buildingTypesToIndex, other.buildingTypesToIndex)) { return false; }
if (!Arrays.deepEquals(queues, other.queues)) { return false; }
if (settings == null) {
if (other.settings != null) { return false; }
} else if (!settings.equals(other.settings)) { return false; }
return true;
}
}