package aima.gui.fx.applications.search;
import java.util.Arrays;
import java.util.List;
import aima.core.environment.nqueens.NQueensBoard;
import aima.core.search.csp.*;
import aima.core.search.csp.examples.NQueensCSP;
import aima.core.search.csp.inference.AC3Strategy;
import aima.core.search.csp.inference.ForwardCheckingStrategy;
import aima.core.util.datastructure.XYLocation;
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.NQueensViewCtrl;
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 CSP solution
* strategies solve the N-Queens problem.
*
* @author Ruediger Lunde
*/
public class NQueensCspApp extends IntegrableApplication {
public static void main(String[] args) {
launch(args);
}
private final static String PARAM_STRATEGY = "strategy";
private final static String PARAM_VAR_SELECT = "varSelect";
private final static String PARAM_VAL_SELECT = "valOrder";
private final static String PARAM_INFERENCE = "inference";
private final static String PARAM_BOARD_SIZE = "boardSize";
private NQueensViewCtrl stateViewCtrl;
private SimulationPaneCtrl simPaneCtrl;
private CSP<Variable, Integer> csp;
private CspSolver<Variable, Integer> solver;
private CspListener.StepCounter<Variable, Integer> stepCounter = new CspListener.StepCounter<>();
@Override
public String getTitle() {
return "N-Queens CSP 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 NQueensViewCtrl(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);
simPaneCtrl.setParam(SimulationPaneCtrl.PARAM_SIM_SPEED, 0);
return root;
}
protected List<Parameter> createParameters() {
Parameter p1 = new Parameter(PARAM_STRATEGY, "Backtracking", "Min-Conflicts");
Parameter p2 = new Parameter(PARAM_VAR_SELECT, "Default", "MRV", "DEG", "MRV&DEG");
Parameter p3 = new Parameter(PARAM_VAL_SELECT, "Default", "LCV");
Parameter p4 = new Parameter(PARAM_INFERENCE, "None", "FC", "AC3");
p2.setDependency(PARAM_STRATEGY, "Backtracking");
p3.setDependency(PARAM_STRATEGY, "Backtracking");
p4.setDependency(PARAM_STRATEGY, "Backtracking");
Parameter p5 = new Parameter(PARAM_BOARD_SIZE, 4, 8, 16, 32, 64);
p5.setDefaultValueIndex(1);
return Arrays.asList(p1, p2, p3, p4, p5);
}
/**
* Displays the initialized board on the state view.
*/
@Override
public void initialize() {
csp = new NQueensCSP(simPaneCtrl.getParamAsInt(PARAM_BOARD_SIZE));
Object strategy = simPaneCtrl.getParamValue(PARAM_STRATEGY);
if (strategy.equals("Backtracking")) {
FlexibleBacktrackingSolver<Variable, Integer> bSolver = new FlexibleBacktrackingSolver<>();
switch ((String) simPaneCtrl.getParamValue(PARAM_VAR_SELECT)) {
case "MRV": bSolver.set(CspHeuristics.mrv()); break;
case "DEG": bSolver.set(CspHeuristics.deg()); break;
case "MRV&DEG": bSolver.set(CspHeuristics.mrvDeg()); break;
}
switch ((String) simPaneCtrl.getParamValue(PARAM_VAL_SELECT)) {
case "LCV": bSolver.set(CspHeuristics.lcv()); break;
}
switch ((String) simPaneCtrl.getParamValue(PARAM_INFERENCE)) {
case "FC": bSolver.set(new ForwardCheckingStrategy<>()); break;
case "AC3": bSolver.set(new AC3Strategy<>()); break;
}
solver = bSolver;
} else if (strategy.equals("Min-Conflicts")) {
solver = new MinConflictsSolver<>(1000);
}
solver.addCspListener(stepCounter);
solver.addCspListener((csp, assign) -> { if (assign != null) updateStateView(getBoard(assign));});
stepCounter.reset();
stateViewCtrl.update(new NQueensBoard(csp.getVariables().size()));
simPaneCtrl.setStatus("");
}
@Override
public void cleanup() {
simPaneCtrl.cancelSimulation();
}
/**
* Starts the experiment.
*/
public void simulate() {
Assignment<Variable, Integer> solution = solver.solve(csp);
if (solution != null) {
NQueensBoard board = getBoard(solution);
stateViewCtrl.update(board);
}
}
private NQueensBoard getBoard(Assignment<Variable, Integer> assignment) {
NQueensBoard board = new NQueensBoard(csp.getVariables().size());
for (Variable var : assignment.getVariables()) {
int col = Integer.parseInt(var.getName().substring(1)) - 1;
int row = assignment.getValue(var) - 1;
board.addQueenAt(new XYLocation(col, row));
}
return board;
}
/**
* Caution: While the background thread should be slowed down, updates of
* the GUI have to be done in the GUI thread!
*/
private void updateStateView(NQueensBoard board) {
Platform.runLater(() -> {
stateViewCtrl.update(board); simPaneCtrl.setStatus(stepCounter.getResults().toString()); });
simPaneCtrl.waitAfterStep();
}
}