package io.vivarium.scripts;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;
import io.vivarium.core.GridWorld;
import io.vivarium.core.simulation.Simulation;
import io.vivarium.serialization.FileIO;
import io.vivarium.serialization.Format;
import io.vivarium.serialization.VivariumObjectCollection;
import io.vivarium.util.UserFacingError;
public class RunSimulation extends CommonsScript
{
private static final String INPUT_FILE = "input";
private static final String OUTPUT_FILE = "output";
private static final String TICKS = "ticks";
private static final String SECONDS = "seconds";
private static final String MINUTES = "minutes";
private static final String HOURS = "hours";
public RunSimulation()
{
super();
}
public RunSimulation(String[] args)
{
super();
CommandLine commandLine = parseArgs(args);
if (commandLine != null)
{
run(commandLine);
}
}
@Override
protected List<Option> getScriptSpecificOptions()
{
LinkedList<Option> options = new LinkedList<>();
options.add(Option
.builder("i")
.required(true)
.longOpt(INPUT_FILE)
.hasArg(true)
.argName("FILE")
.desc("file to load, can be either a world or simulation. At least one limit, either for ticks or time, must be provided if the input is a world.")
.build());
options.add(Option
.builder("o")
.required(true)
.longOpt(OUTPUT_FILE)
.hasArg(true)
.argName("FILE")
.desc("file to save, will be the same type as the provided input")
.build());
options.add(Option
.builder("t")
.required(false)
.longOpt(TICKS)
.hasArg(true)
.argName("TICKS")
.desc("max number of ticks to simulate.")
.build());
options.add(Option
.builder("s")
.required(false)
.longOpt(SECONDS)
.hasArg(true)
.argName("SECONDS")
.desc("max seconds to simulate for, only one time option can be provided")
.build());
options.add(Option
.builder("m")
.required(false)
.longOpt(MINUTES)
.hasArg(true)
.argName("MINUTES")
.desc("max minutes to simulate for, only one time option can be provided")
.build());
options.add(Option
.builder("h")
.required(false)
.longOpt(HOURS)
.hasArg(true)
.argName("HOURS")
.desc("max hours to simulate for, only one time option can be provided")
.build());
return options;
}
@Override
protected void run(CommandLine commandLine)
{
// Load the file
String inputFile = commandLine.getOptionValue(INPUT_FILE);
VivariumObjectCollection collection = FileIO.loadObjectCollection(inputFile, Format.JSON);
GridWorld world = collection.getFirst(GridWorld.class);
if (world == null)
{
String extendedMessage = "input file " + inputFile + " does not contain a world object and cannot be run";
throw new UserFacingError(extendedMessage);
}
// Parse tick / time limits
Long maxTicks = null;
if (commandLine.hasOption(TICKS))
{
maxTicks = Long.parseLong(commandLine.getOptionValue(TICKS));
}
Long maxTime = null;
TimeUnit timeUnit = null;
if (commandLine.hasOption(SECONDS))
{
maxTime = Long.parseLong(commandLine.getOptionValue(SECONDS));
timeUnit = TimeUnit.SECONDS;
}
else if (commandLine.hasOption(MINUTES))
{
maxTime = Long.parseLong(commandLine.getOptionValue(MINUTES));
timeUnit = TimeUnit.MINUTES;
}
else if (commandLine.hasOption(HOURS))
{
maxTime = Long.parseLong(commandLine.getOptionValue(HOURS));
timeUnit = TimeUnit.HOURS;
}
// Run the simulation
run(world, maxTicks, maxTime, timeUnit);
// Save the result;
String outputFile = commandLine.getOptionValue(OUTPUT_FILE);
FileIO.saveSerializer(world, outputFile, Format.JSON);
}
/**
* Simulates a World for at most a given number of ticks and a given length of time. One of maxTicks or maxTime is
* required.
*
* @param world
* The world to simulate.
* @param maxTicks
* The number of ticks to stop the simulation after.
* @param maxTime
* The length of time to stop the simulation after.
* @param timeUnit
* The unit of time that maxTime is measured in. Only checked if maxTime is not null.
* @return nothing, this method changes the World object passed in to it.
*/
public void run(GridWorld world, Long maxTicks, Long maxTime, TimeUnit timeUnit)
{
if (maxTicks != null)
{
if (maxTime != null)
{
// Both ticks and time are specified
Simulation.runForUpTo(world, maxTicks, maxTime, timeUnit);
}
else
{
// Only ticks are specified
Simulation.runForUpTo(world, maxTicks);
}
}
else
{
if (maxTime != null)
{
// Only time is specified
Simulation.runForUpTo(world, maxTime, timeUnit);
}
else
{
// Nothing is specified, this will hang if run
String extendedMessage = "A time limit or tick limit must be specified or a simulation will not halt.";
throw new UserFacingError(extendedMessage);
}
}
}
@Override
protected String getUsageHeader()
{
return "Run a vivarium world for a given number of ticks and then save the output.";
}
@Override
protected String getExtraArgString()
{
return "";
}
public static void main(String[] args)
{
RunSimulation task = new RunSimulation();
task.run(args);
}
}