package aima.gui.fx.applications.search.games;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import aima.core.agent.Action;
import aima.core.environment.eightpuzzle.BidirectionalEightPuzzleProblem;
import aima.core.environment.eightpuzzle.EightPuzzleBoard;
import aima.core.environment.eightpuzzle.ManhattanHeuristicFunction;
import aima.core.environment.eightpuzzle.MisplacedTilleHeuristicFunction;
import aima.core.search.framework.Metrics;
import aima.core.search.framework.SearchForActions;
import aima.core.search.framework.problem.Problem;
import aima.core.search.framework.qsearch.BidirectionalSearch;
import aima.core.search.framework.qsearch.GraphSearch;
import aima.core.search.informed.AStarSearch;
import aima.core.search.informed.GreedyBestFirstSearch;
import aima.core.search.local.Scheduler;
import aima.core.search.local.SimulatedAnnealingSearch;
import aima.core.search.uninformed.BreadthFirstSearch;
import aima.core.search.uninformed.DepthLimitedSearch;
import aima.core.search.uninformed.IterativeDeepeningSearch;
import aima.core.util.CancelableThread;
import aima.gui.fx.framework.IntegrableApplication;
import aima.gui.fx.framework.Parameter;
import aima.gui.fx.framework.SimulationPaneBuilder;
import aima.gui.fx.framework.SimulationPaneCtrl;
import aima.gui.fx.views.EightPuzzleViewCtrl;
import javafx.application.Platform;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
/**
* Integrable application which demonstrates how different search strategies
* solve the Eight Puzzle problem.
*
* @author Ruediger Lunde
*
*/
public class EightPuzzleApp extends IntegrableApplication {
public static void main(String[] args) {
launch(args);
}
/** List of supported search algorithm names. */
protected static List<String> SEARCH_NAMES = new ArrayList<>();
/** List of supported search algorithms. */
protected static List<SearchForActions> SEARCH_ALGOS = new ArrayList<>();
private EightPuzzleBoard board;
/** Adds a new item to the list of supported search algorithms. */
public static void addSearchAlgorithm(String name, SearchForActions algo) {
SEARCH_NAMES.add(name);
SEARCH_ALGOS.add(algo);
}
static {
addSearchAlgorithm("Breadth First Search (Graph Search)", new BreadthFirstSearch(new GraphSearch()));
addSearchAlgorithm("Breadth First Search (Bidirectional Search)",
new BreadthFirstSearch(new BidirectionalSearch()));
addSearchAlgorithm("Depth Limited Search (9)", new DepthLimitedSearch(9));
addSearchAlgorithm("Iterative Deepening Search", new IterativeDeepeningSearch());
addSearchAlgorithm("Greedy Best First Search (MisplacedTileHeuristic)",
new GreedyBestFirstSearch(new GraphSearch(), new MisplacedTilleHeuristicFunction()));
addSearchAlgorithm("Greedy Best First Search (ManhattanHeuristic)",
new GreedyBestFirstSearch(new GraphSearch(), new ManhattanHeuristicFunction()));
addSearchAlgorithm("AStar Search (MisplacedTileHeuristic)",
new AStarSearch(new GraphSearch(), new MisplacedTilleHeuristicFunction()));
addSearchAlgorithm("AStar Search (ManhattanHeuristic)",
new AStarSearch(new GraphSearch(), new ManhattanHeuristicFunction()));
addSearchAlgorithm("Simulated Annealing Search (ManhattanHeuristic)",
new SimulatedAnnealingSearch(new ManhattanHeuristicFunction(), new Scheduler(20, 0.05, 200)));
}
public final static String PARAM_INIT_CONF = "initConf";
public final static String PARAM_STRATEGY = "strategy";
private EightPuzzleViewCtrl stateViewCtrl;
private SimulationPaneCtrl simPaneCtrl;
public EightPuzzleApp() {
}
@Override
public String getTitle() {
return "Eight Puzzle App";
}
/**
* Defines state view, parameters, and call-back functions and calls the
* simulation pane builder to create layout and controller objects.
*/
@Override
public Pane createRootPane() {
BorderPane root = new BorderPane();
StackPane stateView = new StackPane();
stateViewCtrl = new EightPuzzleViewCtrl(stateView);
List<Parameter> params = createParameters();
SimulationPaneBuilder builder = new SimulationPaneBuilder();
builder.defineParameters(params);
builder.defineStateView(stateView);
builder.defineInitMethod(this::initialize);
builder.defineSimMethod(this::simulate);
simPaneCtrl = builder.getResultFor(root);
return root;
}
protected List<Parameter> createParameters() {
Parameter p1 = new Parameter(PARAM_INIT_CONF, "Three Moves", "Medium", "Extreme", "Random");
Parameter p2 = new Parameter(PARAM_STRATEGY, (Object[]) SEARCH_NAMES.toArray());
return Arrays.asList(p1, p2);
}
/** Displays the initialized board on the state view. */
@Override
public void initialize() {
board = null;
switch (simPaneCtrl.getParamValueIndex(PARAM_INIT_CONF)) {
case 0: // three moves
board = new EightPuzzleBoard(new int[] { 1, 2, 5, 3, 4, 0, 6, 7, 8 });
break;
case 1: // medium
board = new EightPuzzleBoard(new int[] { 1, 4, 2, 7, 5, 8, 3, 0, 6 });
break;
case 2: // extreme
board = new EightPuzzleBoard(new int[] { 0, 8, 7, 6, 5, 4, 3, 2, 1 });
break;
case 3: // random
board = new EightPuzzleBoard(new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8 });
Random r = new Random(System.currentTimeMillis());
for (int i = 0; i < 200; i++) {
switch (r.nextInt(4)) {
case 0:
board.moveGapUp();
break;
case 1:
board.moveGapDown();
break;
case 2:
board.moveGapLeft();
break;
case 3:
board.moveGapRight();
break;
}
}
}
stateViewCtrl.initialize(board);
}
@Override
public void cleanup() {
simPaneCtrl.cancelSimulation();
}
/** Starts the experiment. */
public void simulate() {
int strategyIdx = simPaneCtrl.getParamValueIndex(PARAM_STRATEGY);
Problem problem = new BidirectionalEightPuzzleProblem(board);
SearchForActions search = SEARCH_ALGOS.get(strategyIdx);
List<Action> actions = search.findActions(problem);
for (Action action : actions) {
if (action == EightPuzzleBoard.UP)
board.moveGapUp();
else if (action == EightPuzzleBoard.DOWN)
board.moveGapDown();
else if (action == EightPuzzleBoard.LEFT)
board.moveGapLeft();
else if (action == EightPuzzleBoard.RIGHT)
board.moveGapRight();
Metrics m = new Metrics();
m.set("manhattanHeuristic", new ManhattanHeuristicFunction().h(board));
updateStateView(m);
if (CancelableThread.currIsCanceled())
break;
simPaneCtrl.waitAfterStep();
}
updateStateView(search.getMetrics());
}
/**
* Caution: While the background thread should be slowed down, updates of
* the GUI have to be done in the GUI thread!
*/
private void updateStateView(Metrics metrics) {
Platform.runLater(() -> updateStateViewLater(metrics));
simPaneCtrl.waitAfterStep();
}
/**
* Must be called by the GUI thread!
*/
private void updateStateViewLater(Metrics metrics) {
stateViewCtrl.update();
if (metrics != null)
simPaneCtrl.setStatus(metrics.toString());
}
}