com.badlogic.gdx.ai.btree.utils.DistributionAdapters Maven / Gradle / Ivy
/*******************************************************************************
* Copyright 2014 See AUTHORS file.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.badlogic.gdx.ai.btree.utils;
import java.util.StringTokenizer;
import com.badlogic.gdx.ai.utils.random.ConstantDoubleDistribution;
import com.badlogic.gdx.ai.utils.random.ConstantFloatDistribution;
import com.badlogic.gdx.ai.utils.random.ConstantIntegerDistribution;
import com.badlogic.gdx.ai.utils.random.ConstantLongDistribution;
import com.badlogic.gdx.ai.utils.random.Distribution;
import com.badlogic.gdx.ai.utils.random.DoubleDistribution;
import com.badlogic.gdx.ai.utils.random.FloatDistribution;
import com.badlogic.gdx.ai.utils.random.GaussianDoubleDistribution;
import com.badlogic.gdx.ai.utils.random.GaussianFloatDistribution;
import com.badlogic.gdx.ai.utils.random.IntegerDistribution;
import com.badlogic.gdx.ai.utils.random.LongDistribution;
import com.badlogic.gdx.ai.utils.random.TriangularDoubleDistribution;
import com.badlogic.gdx.ai.utils.random.TriangularFloatDistribution;
import com.badlogic.gdx.ai.utils.random.TriangularIntegerDistribution;
import com.badlogic.gdx.ai.utils.random.TriangularLongDistribution;
import com.badlogic.gdx.ai.utils.random.UniformDoubleDistribution;
import com.badlogic.gdx.ai.utils.random.UniformFloatDistribution;
import com.badlogic.gdx.ai.utils.random.UniformIntegerDistribution;
import com.badlogic.gdx.ai.utils.random.UniformLongDistribution;
import com.badlogic.gdx.utils.ObjectMap;
/** @author davebaol */
public class DistributionAdapters {
/** Thrown to indicate that the application has attempted to convert a string to one of the distribution types, but that the
* string does not have the appropriate format.
*
* @author davebaol */
public static class DistributionFormatException extends RuntimeException {
/** Constructs a DistributionFormatException
with no detail message. */
public DistributionFormatException () {
super();
}
/** Constructs a DistributionFormatException
with the specified detail message.
*
* @param s the detail message. */
public DistributionFormatException (String s) {
super(s);
}
/** Constructs a DistributionFormatException
with the specified detail message and cause.
*
* Note that the detail message associated with cause
is not automatically incorporated in this
* exception's detail message.
*
* @param message the detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method).
* @param cause the cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null
* value is permitted, and indicates that the cause is nonexistent or unknown.) */
public DistributionFormatException (String message, Throwable cause) {
super(message, cause);
}
/** Constructs a DistributionFormatException
with the specified cause and a detail message of
* (cause==null ? null : cause.toString()) (which typically contains the class and detail message of cause
* ). This constructor is useful for exceptions that are little more than wrappers for other throwables.
*
* @param cause the cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null
* value is permitted, and indicates that the cause is nonexistent or unknown.) */
public DistributionFormatException (Throwable cause) {
super(cause);
}
}
public abstract static class Adapter {
final String category;
final Class> type;
public Adapter (String category, Class> type) {
this.category = category;
this.type = type;
}
public abstract D toDistribution (String[] args);
public abstract String[] toParameters (D distribution);
public static double parseDouble (String v) {
try {
return Double.parseDouble(v);
} catch (NumberFormatException nfe) {
throw new DistributionFormatException("Not a double value: " + v, nfe);
}
}
public static float parseFloat (String v) {
try {
return Float.parseFloat(v);
} catch (NumberFormatException nfe) {
throw new DistributionFormatException("Not a float value: " + v, nfe);
}
}
public static int parseInteger (String v) {
try {
return Integer.parseInt(v);
} catch (NumberFormatException nfe) {
throw new DistributionFormatException("Not an int value: " + v, nfe);
}
}
public static long parseLong (String v) {
try {
return Long.parseLong(v);
} catch (NumberFormatException nfe) {
throw new DistributionFormatException("Not a long value: " + v, nfe);
}
}
}
public abstract static class DoubleAdapter extends Adapter {
public DoubleAdapter (String category) {
super(category, DoubleDistribution.class);
}
}
public abstract static class FloatAdapter extends Adapter {
public FloatAdapter (String category) {
super(category, FloatDistribution.class);
}
}
public abstract static class IntegerAdapter extends Adapter {
public IntegerAdapter (String category) {
super(category, IntegerDistribution.class);
}
}
public abstract static class LongAdapter extends Adapter {
public LongAdapter (String category) {
super(category, LongDistribution.class);
}
}
private static final ObjectMap, Adapter>> adapters = new ObjectMap, Adapter>>();
static {
//
// Constant distributions
//
adapters.put(ConstantDoubleDistribution.class, new DoubleAdapter("constant") {
@Override
public ConstantDoubleDistribution toDistribution (String[] args) {
if (args.length != 1) throw invalidNumberOfArgumentsException(args.length, 1);
return new ConstantDoubleDistribution(parseDouble(args[0]));
}
@Override
public String[] toParameters (ConstantDoubleDistribution distribution) {
return new String[] {Double.toString(distribution.getValue())};
}
});
adapters.put(ConstantFloatDistribution.class, new FloatAdapter("constant") {
@Override
public ConstantFloatDistribution toDistribution (String[] args) {
if (args.length != 1) throw invalidNumberOfArgumentsException(args.length, 1);
return new ConstantFloatDistribution(parseFloat(args[0]));
}
@Override
public String[] toParameters (ConstantFloatDistribution distribution) {
return new String[] {Float.toString(distribution.getValue())};
}
});
adapters.put(ConstantIntegerDistribution.class, new IntegerAdapter("constant") {
@Override
public ConstantIntegerDistribution toDistribution (String[] args) {
if (args.length != 1) throw invalidNumberOfArgumentsException(args.length, 1);
return new ConstantIntegerDistribution(parseInteger(args[0]));
}
@Override
public String[] toParameters (ConstantIntegerDistribution distribution) {
return new String[] {Integer.toString(distribution.getValue())};
}
});
adapters.put(ConstantLongDistribution.class, new LongAdapter("constant") {
@Override
public ConstantLongDistribution toDistribution (String[] args) {
if (args.length != 1) throw invalidNumberOfArgumentsException(args.length, 1);
return new ConstantLongDistribution(parseLong(args[0]));
}
@Override
public String[] toParameters (ConstantLongDistribution distribution) {
return new String[] {Long.toString(distribution.getValue())};
}
});
//
// Gaussian distributions
//
adapters.put(GaussianDoubleDistribution.class, new DoubleAdapter("gaussian") {
@Override
public GaussianDoubleDistribution toDistribution (String[] args) {
if (args.length != 2) throw invalidNumberOfArgumentsException(args.length, 2);
return new GaussianDoubleDistribution(parseDouble(args[0]), parseDouble(args[1]));
}
@Override
public String[] toParameters (GaussianDoubleDistribution distribution) {
return new String[] {Double.toString(distribution.getMean()), Double.toString(distribution.getStandardDeviation())};
}
});
adapters.put(GaussianFloatDistribution.class, new FloatAdapter("gaussian") {
@Override
public GaussianFloatDistribution toDistribution (String[] args) {
if (args.length != 2) throw invalidNumberOfArgumentsException(args.length, 2);
return new GaussianFloatDistribution(parseFloat(args[0]), parseFloat(args[1]));
}
@Override
public String[] toParameters (GaussianFloatDistribution distribution) {
return new String[] {Float.toString(distribution.getMean()), Float.toString(distribution.getStandardDeviation())};
}
});
//
// Triangular distributions
//
adapters.put(TriangularDoubleDistribution.class, new DoubleAdapter("triangular") {
@Override
public TriangularDoubleDistribution toDistribution (String[] args) {
switch (args.length) {
case 1:
return new TriangularDoubleDistribution(parseDouble(args[0]));
case 2:
return new TriangularDoubleDistribution(parseDouble(args[0]), parseDouble(args[1]));
case 3:
return new TriangularDoubleDistribution(parseDouble(args[0]), parseDouble(args[1]), parseDouble(args[2]));
default:
throw invalidNumberOfArgumentsException(args.length, 1, 2, 3);
}
}
@Override
public String[] toParameters (TriangularDoubleDistribution distribution) {
return new String[] {Double.toString(distribution.getLow()), Double.toString(distribution.getHigh()),
Double.toString(distribution.getMode())};
}
});
adapters.put(TriangularFloatDistribution.class, new FloatAdapter("triangular") {
@Override
public TriangularFloatDistribution toDistribution (String[] args) {
switch (args.length) {
case 1:
return new TriangularFloatDistribution(parseFloat(args[0]));
case 2:
return new TriangularFloatDistribution(parseFloat(args[0]), parseFloat(args[1]));
case 3:
return new TriangularFloatDistribution(parseFloat(args[0]), parseFloat(args[1]), parseFloat(args[2]));
default:
throw invalidNumberOfArgumentsException(args.length, 1, 2, 3);
}
}
@Override
public String[] toParameters (TriangularFloatDistribution distribution) {
return new String[] {Float.toString(distribution.getLow()), Float.toString(distribution.getHigh()),
Float.toString(distribution.getMode())};
}
});
adapters.put(TriangularIntegerDistribution.class, new IntegerAdapter("triangular") {
@Override
public TriangularIntegerDistribution toDistribution (String[] args) {
switch (args.length) {
case 1:
return new TriangularIntegerDistribution(parseInteger(args[0]));
case 2:
return new TriangularIntegerDistribution(parseInteger(args[0]), parseInteger(args[1]));
case 3:
return new TriangularIntegerDistribution(parseInteger(args[0]), parseInteger(args[1]), Float.valueOf(args[2]));
default:
throw invalidNumberOfArgumentsException(args.length, 1, 2, 3);
}
}
@Override
public String[] toParameters (TriangularIntegerDistribution distribution) {
return new String[] {Integer.toString(distribution.getLow()), Integer.toString(distribution.getHigh()),
Float.toString(distribution.getMode())};
}
});
adapters.put(TriangularLongDistribution.class, new LongAdapter("triangular") {
@Override
public TriangularLongDistribution toDistribution (String[] args) {
switch (args.length) {
case 1:
return new TriangularLongDistribution(parseLong(args[0]));
case 2:
return new TriangularLongDistribution(parseLong(args[0]), parseLong(args[1]));
case 3:
return new TriangularLongDistribution(parseLong(args[0]), parseLong(args[1]), parseDouble(args[2]));
default:
throw invalidNumberOfArgumentsException(args.length, 1, 2, 3);
}
}
@Override
public String[] toParameters (TriangularLongDistribution distribution) {
return new String[] {Long.toString(distribution.getLow()), Long.toString(distribution.getHigh()),
Double.toString(distribution.getMode())};
}
});
//
// Uniform distributions
//
adapters.put(UniformDoubleDistribution.class, new DoubleAdapter("uniform") {
@Override
public UniformDoubleDistribution toDistribution (String[] args) {
switch (args.length) {
case 1:
return new UniformDoubleDistribution(parseDouble(args[0]));
case 2:
return new UniformDoubleDistribution(parseDouble(args[0]), parseDouble(args[1]));
default:
throw invalidNumberOfArgumentsException(args.length, 1, 2);
}
}
@Override
public String[] toParameters (UniformDoubleDistribution distribution) {
return new String[] {Double.toString(distribution.getLow()), Double.toString(distribution.getHigh())};
}
});
adapters.put(UniformFloatDistribution.class, new FloatAdapter("uniform") {
@Override
public UniformFloatDistribution toDistribution (String[] args) {
switch (args.length) {
case 1:
return new UniformFloatDistribution(parseFloat(args[0]));
case 2:
return new UniformFloatDistribution(parseFloat(args[0]), parseFloat(args[1]));
default:
throw invalidNumberOfArgumentsException(args.length, 1, 2);
}
}
@Override
public String[] toParameters (UniformFloatDistribution distribution) {
return new String[] {Float.toString(distribution.getLow()), Float.toString(distribution.getHigh())};
}
});
adapters.put(UniformIntegerDistribution.class, new IntegerAdapter("uniform") {
@Override
public UniformIntegerDistribution toDistribution (String[] args) {
switch (args.length) {
case 1:
return new UniformIntegerDistribution(parseInteger(args[0]));
case 2:
return new UniformIntegerDistribution(parseInteger(args[0]), parseInteger(args[1]));
default:
throw invalidNumberOfArgumentsException(args.length, 1, 2);
}
}
@Override
public String[] toParameters (UniformIntegerDistribution distribution) {
return new String[] {Integer.toString(distribution.getLow()), Integer.toString(distribution.getHigh())};
}
});
adapters.put(UniformLongDistribution.class, new LongAdapter("uniform") {
@Override
public UniformLongDistribution toDistribution (String[] args) {
switch (args.length) {
case 1:
return new UniformLongDistribution(parseLong(args[0]));
case 2:
return new UniformLongDistribution(parseLong(args[0]), parseLong(args[1]));
default:
throw invalidNumberOfArgumentsException(args.length, 1, 2);
}
}
@Override
public String[] toParameters (UniformLongDistribution distribution) {
return new String[] {Long.toString(distribution.getLow()), Long.toString(distribution.getHigh())};
}
});
}
ObjectMap, Adapter>> map;
ObjectMap, ObjectMap>> typeMap;
public DistributionAdapters () {
this.map = new ObjectMap, Adapter>>();
this.typeMap = new ObjectMap, ObjectMap>>();
for (ObjectMap.Entry, Adapter>> e : adapters.entries())
add(e.key, e.value);
}
public final void add (Class> clazz, Adapter> adapter) {
map.put(clazz, adapter);
ObjectMap> m = typeMap.get(adapter.type);
if (m == null) {
m = new ObjectMap>();
typeMap.put(adapter.type, m);
}
m.put(adapter.category, adapter);
}
@SuppressWarnings("unchecked")
public T toDistribution (String value, Class clazz) {
StringTokenizer st = new StringTokenizer(value, ", \t\f");
if (!st.hasMoreTokens()) throw new DistributionFormatException("Missing ditribution type");
String type = st.nextToken();
ObjectMap> categories = typeMap.get(clazz);
Adapter converter = (Adapter)categories.get(type);
if (converter == null)
throw new DistributionFormatException("Cannot create a '" + clazz.getSimpleName() + "' of type '" + type + "'");
String[] args = new String[st.countTokens()];
for (int i = 0; i < args.length; i++)
args[i] = st.nextToken();
return converter.toDistribution(args);
}
@SuppressWarnings({"rawtypes", "unchecked"})
public String toString (Distribution distribution) {
Adapter adapter = map.get(distribution.getClass());
String args[] = adapter.toParameters(distribution);
String out = adapter.category;
for (String a : args)
out += "," + a;
return out;
}
private static DistributionFormatException invalidNumberOfArgumentsException (int found, int... expected) {
String message = "Found " + found + " arguments in triangular distribution; expected ";
if (expected.length < 2)
message += expected.length;
else {
String sep = "";
int i = 0;
while (i < expected.length - 1) {
message += sep + expected[i++];
sep = ", ";
}
message += " or " + expected[i];
}
return new DistributionFormatException(message);
}
}