/*
* Copyright © 2010 by Ondrej Skalicka. All Rights Reserved
*/
package cz.cvut.felk.cig.jcop.problem.bucket;
import cz.cvut.felk.cig.jcop.problem.*;
import java.util.ArrayList;
import java.util.List;
/**
* Bucket problem.
* <p/>
* Bucket problem is defined by having fixed number of buckets. Each of these buckets have their maximum capacity and
* starting capacity. Then each bucket could be fully filled or spilled empty. Furthermore each bucket's contents could
* be poured into another one using simple rule - if all contents from target fits to destination bucket, pour it all.
* If not, pour only what is possible and leave the remainder in source bucket.
* <p/>
* TODO implement loaders from file or string
*
* @author Ondrej Skalicka
* @see <a href="http://service.felk.cvut.cz/courses/X36PAA/Kyble.htm"> Bucket problem on felk.cvut.fel</a>
* @see FillOperation Fill operation
* @see SpillOperation Spill operation
* @see PourOperation Pour operation
*/
public class Bucket extends BaseProblem implements Problem, DestinationProblem, StartingConfigurationProblem {
/**
* List of buckets assigned to this problem instance
*/
protected List<BucketItem> buckets;
/**
* Starting configuration for bucket problem
*/
protected Configuration startingConfiguration;
/**
* Configurations considered as solution
*/
protected List<Configuration> destinationConfigurations;
/**
* Fill operations - one for each bucket. Index of list is equal to {@link BucketItem#index}
*
* @see cz.cvut.felk.cig.jcop.problem.bucket.FillOperation
*/
protected List<FillOperation> fillOperations;
/**
* Spill operations - one for each bucket. Index of list is equal to {@link BucketItem#index}
*
* @see cz.cvut.felk.cig.jcop.problem.bucket.SpillOperation
*/
protected List<SpillOperation> spillOperations;
/**
* Pour operations - each bucket has its own list (since one bucket could be poured to all other buckets. First
* index of list is equal to {@link BucketItem#index}. Second is equal to {@link BucketItem#index} of destination
* bucket iff destination bucket has {@link BucketItem#index} lower than source bucket. If it is higher, index in
* list is equal to destination's bucket {@link BucketItem#index}-1.
*/
protected List<List<PourOperation>> pourOperations;
/**
* Creates new bucket problem from given capacity, starting contents and destination contents.
* <p/>
* Note that this problem has Ο(n<sup>2</sup>) complexity just to create it (every bucket has n-1 operations
* to other buckets).
*
* @param capacity capacity for bucket 0 .. n-1
* @param startingContents starting contents for bucket 0 .. n-1
* @param destinationContents target contents for bucket 0 .. n-1
* @throws ProblemFormatException if three input arrays are not of same size or they are empty
*/
public Bucket(int[] capacity, int[] startingContents, int[] destinationContents) throws ProblemFormatException {
StringBuffer labelStringBufferA = new StringBuffer("start={");
StringBuffer labelStringBufferB = new StringBuffer("},destination={");
StringBuffer labelStringBufferC = new StringBuffer("},capacity={");
this.dimension = capacity.length;
// check validity of parameters
if (this.dimension < 1)
throw new ProblemFormatException("Invalid dimension " + this.dimension);
if (destinationContents.length != this.dimension) {
throw new ProblemFormatException(String.format(
"Destination contents has wrong capacity, expected %d, given %d",
this.dimension, destinationContents.length));
}
if (startingContents.length != this.dimension) {
throw new ProblemFormatException(String.format(
"Starting contents has wrong capacity, expected %s, given %d",
startingContents.length, this.dimension));
}
// create buckets
this.buckets = new ArrayList<BucketItem>(this.dimension);
List<Integer> startingConfigurationAttributes = new ArrayList<Integer>(this.dimension);
List<Integer> destinationConfigurationAttributes = new ArrayList<Integer>(this.dimension);
for (int i = 0; i < this.dimension; i++) {
this.buckets.add(new BucketItem(i, capacity[i]));
startingConfigurationAttributes.add(startingContents[i]);
destinationConfigurationAttributes.add(destinationContents[i]);
// add starting/destination/contents to label
labelStringBufferA.append(i == 0 ? "" : ",").append(startingContents[i]);
labelStringBufferB.append(i == 0 ? "" : ",").append(destinationContents[i]);
labelStringBufferC.append(i == 0 ? "" : ",").append(capacity[i]);
}
labelStringBufferA.append(labelStringBufferB);
labelStringBufferA.append(labelStringBufferC);
labelStringBufferA.append("}");
this.setLabel(labelStringBufferA.toString());
// create starting configuration
this.startingConfiguration = new Configuration(startingConfigurationAttributes, "Bucket starting configuration");
// create destination configuration
this.destinationConfigurations = new ArrayList<Configuration>(1);
this.destinationConfigurations.add(new Configuration(destinationConfigurationAttributes, "Bucket starting configuration"));
// create fill & spill operations
this.fillOperations = new ArrayList<FillOperation>(this.dimension);
this.spillOperations = new ArrayList<SpillOperation>(this.dimension);
for (int i = 0; i < this.dimension; i++) {
this.fillOperations.add(new FillOperation(this.buckets.get(i)));
this.spillOperations.add(new SpillOperation(this.buckets.get(i)));
}
// create pour operations
this.pourOperations = new ArrayList<List<PourOperation>>(this.dimension);
for (int i = 0; i < this.dimension; ++i) {
List<PourOperation> tmp = new ArrayList<PourOperation>(this.dimension - 1);
for (int j = 0; j < this.dimension; ++j) {
if (i != j)
tmp.add(new PourOperation(this.buckets.get(i), this.buckets.get(j)));
}
this.pourOperations.add(tmp);
}
}
/* Problem interface */
public boolean isSolution(Configuration configuration) {
return configuration.equals(this.destinationConfigurations.get(0));
}
public BucketIterator getOperationIterator(Configuration configuration) {
return new BucketIterator(configuration, this);
}
public Fitness getDefaultFitness() {
return new BucketFitness(this);
}
/* DestinationProblem interface */
public List<Configuration> getDestinations() {
return this.destinationConfigurations;
}
/* StartingConfigurationProblem interface */
public Configuration getStartingConfiguration() {
return this.startingConfiguration;
}
}