diff --git a/.idea/.gitignore b/.idea/.gitignore
index 13566b8..98f4b2f 100644
--- a/.idea/.gitignore
+++ b/.idea/.gitignore
@@ -1,8 +1 @@
-# Default ignored files
-/shelf/
/workspace.xml
-# Editor-based HTTP Client requests
-/httpRequests/
-# Datasource local storage ignored files
-/dataSources/
-/dataSources.local.xml
diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml
deleted file mode 100644
index 2b63946..0000000
--- a/.idea/uiDesigner.xml
+++ /dev/null
@@ -1,124 +0,0 @@
-
-
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
-
-
-
- -
-
-
-
-
-
- -
-
-
-
-
-
- -
-
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
-
-
- -
-
-
- -
-
-
-
-
-
\ No newline at end of file
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..bf64647
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,12 @@
+{
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "type": "java",
+ "name": "Labyrinthe",
+ "request": "launch",
+ "mainClass": "Main",
+ "projectName": "SAE21_2022_c8f95a52"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Makefile b/Makefile
index e48f2d5..31e9758 100644
--- a/Makefile
+++ b/Makefile
@@ -1,3 +1,11 @@
+# If the first argument is "run"...
+ifeq (run,$(firstword $(MAKECMDGOALS)))
+ # use the rest as arguments for "run"
+ RUN_ARGS := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))
+ # ...and turn them into do-nothing targets
+ $(eval $(RUN_ARGS):;@:)
+endif
+
### VARIABLES ###
JC = javac
@@ -21,7 +29,7 @@ $(OUTDIR)/Main.class : $(OFILES)
### REGLES OPTIONNELLES ###
run : $(OUTDIR)/Main.class
- ${JVM} ${JVMFLAGS} -cp $(OUTDIR) Main
+ ${JVM} ${JVMFLAGS} -cp $(OUTDIR) Main $(RUN_ARGS)
clean :
-rm -rf $(OUTDIR)
diff --git a/SAE21_2022.iml b/SAE21_2022.iml
index c90834f..22eec9e 100644
--- a/SAE21_2022.iml
+++ b/SAE21_2022.iml
@@ -1,6 +1,7 @@
-
+
+
diff --git a/src/Algo.java b/src/Algo.java
index 8258902..a7cb282 100644
--- a/src/Algo.java
+++ b/src/Algo.java
@@ -10,8 +10,4 @@ public interface Algo {
*/
void nextMove();
- /**
- * Resets the algorithm
- */
- void reset();
}
diff --git a/src/AlgoType.java b/src/AlgoType.java
new file mode 100644
index 0000000..cf57464
--- /dev/null
+++ b/src/AlgoType.java
@@ -0,0 +1,17 @@
+/**
+ * Enum for the different types of algorithms
+ * @version 1.0
+ * @author Amir Daouadi
+ * @author Lyanis Souidi
+ */
+public enum AlgoType {
+ /**
+ * The random algorithm
+ */
+ RANDOM,
+
+ /**
+ * The deterministic algorithm
+ */
+ DETERMINISTIC,
+}
diff --git a/src/AutoSimulation.java b/src/AutoSimulation.java
new file mode 100644
index 0000000..b77b2f1
--- /dev/null
+++ b/src/AutoSimulation.java
@@ -0,0 +1,98 @@
+/**
+ * This class is used to store the data of the auto simulations
+ * @version 1.0
+ * @author Amir Daouadi
+ * @author Lyanis Souidi
+ */
+public class AutoSimulation {
+ /**
+ * The grid to use for the simulations
+ */
+ private final Grid grid;
+
+ /**
+ * The algorithm type to use for the simulations
+ */
+ private final AlgoType algoType;
+
+ /**
+ * The simulations
+ */
+ private final Simulation[] simulations = new Simulation[100];
+
+ /**
+ * Constructor
+ * @param grid The grid to use for the simulations
+ * @param algoType The algorithm type to use for the simulations
+ */
+ public AutoSimulation(Grid grid, AlgoType algoType) {
+ this.grid = grid;
+ this.algoType = algoType;
+
+ for (int i = 0; i < this.simulations.length; i++) {
+ simulations[i] = new Simulation();
+ }
+ }
+
+ /**
+ * Get the grid used for the simulations
+ * @return The grid
+ */
+ public Grid getGrid() {
+ return this.grid;
+ }
+
+ /**
+ * Get the algorithm type used for the simulations
+ * @return The algorithm type
+ */
+ public AlgoType getAlgoType() {
+ return this.algoType;
+ }
+
+ /**
+ * Get the simulations
+ * @return The simulations
+ */
+ public Simulation[] getSimulations() {
+ return this.simulations;
+ }
+
+ /**
+ * Get the average number of moves of the simulations
+ * @return The average number of moves
+ */
+ public float getAverageMoves() {
+ int endedSimulations = 0;
+ float averageMoves = 0;
+
+ for (Simulation simulation : simulations) {
+ if (simulation.isEnded()) {
+ endedSimulations++;
+ averageMoves += simulation.getMoves();
+ }
+ }
+
+ if (endedSimulations == 0) return 0;
+
+ averageMoves = averageMoves / endedSimulations;
+
+ return averageMoves;
+ }
+
+ /**
+ * Get the number of ended simulations
+ * @return The number of ended simulations
+ */
+ public int getNumberOfEndedSimulations() {
+ int endedSimulations = 0;
+
+ for (Simulation simulation : simulations) {
+ if (simulation.isEnded()) {
+ endedSimulations++;
+ }
+ }
+
+ return endedSimulations;
+ }
+}
diff --git a/src/AutoSimulationController.java b/src/AutoSimulationController.java
new file mode 100644
index 0000000..20b0fe6
--- /dev/null
+++ b/src/AutoSimulationController.java
@@ -0,0 +1,51 @@
+/**
+ * Controller for the automatic simulation
+ * @version 1.0
+ * @author Amir Daouadi
+ * @author Lyanis Souidi
+ */
+public class AutoSimulationController {
+ /**
+ * The automatic simulation view
+ */
+ private final AutoSimulationView view;
+
+ /**
+ * The automatic simulation model
+ */
+ private final AutoSimulation model;
+
+ /**
+ * Constructor
+ * @param view The automatic simulation view
+ * @param model The automatic simulation model
+ */
+ public AutoSimulationController(AutoSimulationView view, AutoSimulation model) {
+ this.view = view;
+ this.model = model;
+
+ new Thread(this::run).start();
+ }
+
+ /**
+ * Run the simulations
+ */
+ public void run() {
+ for (Simulation simulation : this.model.getSimulations()) {
+ if (!simulation.isEnded()) {
+ Algo algo;
+ if (this.model.getAlgoType() == AlgoType.RANDOM) {
+ algo = new RandomAlgo(this.model.getGrid(), simulation);
+ } else {
+ algo = new DeterministicAlgo(this.model.getGrid(), simulation);
+ }
+ while (!simulation.isEnded()) {
+ algo.nextMove();
+ this.view.repaint();
+ }
+
+ this.model.getGrid().reset();
+ }
+ }
+ }
+}
diff --git a/src/AutoSimulationView.java b/src/AutoSimulationView.java
index 32d9b44..3802572 100644
--- a/src/AutoSimulationView.java
+++ b/src/AutoSimulationView.java
@@ -2,64 +2,51 @@ import javax.swing.*;
import java.awt.*;
/**
- * The view for the auto simulation
+ * The view for the automatic simulation
* Display directly the success rate and the average number of moves of the simulations
- * @version 1.0
+ * @version 1.1
* @author Amir Daouadi
* @author Lyanis Souidi
*/
public class AutoSimulationView extends JPanel {
- /**
- * The simulations to display
- */
- private Simulation[] simulations;
- /**
- * The success rate of the simulations
- */
- private float success = 0;
-
- /**
- * The average number of moves of the simulations
- */
- private float moves = 0;
+ public final AutoSimulation model;
/**
* Constructor
- * @param simulations The simulations to display
+ * @param model The automatic simulation model
*/
- public AutoSimulationView(Simulation[] simulations) {
+ public AutoSimulationView(AutoSimulation model) {
super();
- this.simulations = simulations;
+ this.model = model;
this.setOpaque(false);
this.setPreferredSize(new Dimension(700, 500));
}
- /**
- * Calculate the success rate and the average number of moves
- */
- private void calculate() {
- for (Simulation simulation : simulations) {
- this.success += simulation.isSuccess() ? 1 : 0;
- this.moves += simulation.getMoves();
- }
-
- this.success = (this.success / simulations.length) * 100;
-
- this.moves = this.moves / simulations.length;
- }
-
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
- calculate();
- g.setColor(Color.BLACK);
+
+ if (this.model.getSimulations()[0].getMoves() == 0) return;
+
g.setFont(new Font("Arial", Font.PLAIN, 20));
FontMetrics metrics = g.getFontMetrics(g.getFont());
- String successStr = "Taux de réussite : " + this.success + "%";
- g.drawString(successStr, (getWidth() - metrics.stringWidth(successStr)) / 2, ((getHeight() - metrics.getHeight()) / 2 + metrics.getAscent()) - 50);
+ int y = 0;
- String movesStr = "Nombre de mouvements moyen : " + this.moves;
- g.drawString(movesStr, (getWidth() - metrics.stringWidth(movesStr)) / 2, ((getHeight() - metrics.getHeight()) / 2 + metrics.getAscent()) + 50);
+ int endedSimulations = this.model.getNumberOfEndedSimulations();
+ int totalSimulations = this.model.getSimulations().length;
+ int index = endedSimulations >= totalSimulations ? totalSimulations - 1 : endedSimulations;
+ Simulation lastSimulation = this.model.getSimulations()[index];
+
+ if (!lastSimulation.isEnded()) {
+ y = 50;
+ g.setColor(Color.RED);
+ String simulationStr = "Simulation " + endedSimulations + "/" + totalSimulations + " : " + lastSimulation.getMoves() + " mouvements";
+ g.drawString(simulationStr, (getWidth() - metrics.stringWidth(simulationStr)) / 2, ((getHeight() - metrics.getHeight()) / 2 + metrics.getAscent()) - 50);
+ }
+
+ g.setColor(Color.BLACK);
+ String movesStr = "Nombre de mouvements moyen : " + this.model.getAverageMoves();
+ g.drawString(movesStr, (getWidth() - metrics.stringWidth(movesStr)) / 2, ((getHeight() - metrics.getHeight()) / 2 + metrics.getAscent()) + y);
}
}
diff --git a/src/DeterministicAlgo.java b/src/DeterministicAlgo.java
new file mode 100644
index 0000000..db6f8ac
--- /dev/null
+++ b/src/DeterministicAlgo.java
@@ -0,0 +1,74 @@
+import java.util.Stack;
+
+/**
+ * Deterministic algorithm
+ * @version 0.1
+ * @author Amir Daouadi
+ * @author Lyanis Souidi
+ */
+public class DeterministicAlgo implements Algo {
+ /**
+ * The grid model
+ */
+ private final Grid grid;
+
+ /**
+ * The simulation model
+ */
+ private final Simulation simulation;
+
+ /**
+ * The Thésée model
+ */
+ private final Thesee thesee;
+
+ /**
+ * The Thésée controller
+ */
+ private final TheseeController theseeController;
+
+ /**
+ * The directions stack used to go back
+ */
+ private final Stack directions = new Stack<>();
+
+ /**
+ * Constructor
+ * @param grid The grid model
+ * @param simulation The simulation model
+ */
+ public DeterministicAlgo(Grid grid, Simulation simulation) {
+ this.grid = grid;
+ this.simulation = simulation;
+ this.thesee = this.grid.getThesee();
+ this.theseeController = new TheseeController(this.thesee);
+ this.thesee.getSquare().setVisited(true);
+ }
+
+ /**
+ * Makes the next move of the algorithm
+ */
+ @Override
+ public void nextMove() {
+ if (this.simulation.isEnded()) return;
+ Direction[] availableDirection = this.theseeController.getAvailableDirections();
+
+ Direction nextUnvisitedDirection = null;
+ for (Direction direction : availableDirection) {
+ try {
+ if (!this.thesee.getSquare(direction).isVisited()) {
+ nextUnvisitedDirection = direction;
+ break;
+ }
+ } catch (Exception ignored) {}
+ }
+ if (nextUnvisitedDirection != null) {
+ if (this.theseeController.move(nextUnvisitedDirection, this.simulation)) this.directions.push(nextUnvisitedDirection);
+ } else this.theseeController.move(this.directions.pop().opposite(), this.simulation);
+
+ if (!this.simulation.isEnded() && this.grid.isEnded()) {
+ this.simulation.setEnded();
+ }
+ }
+
+}
diff --git a/src/Direction.java b/src/Direction.java
new file mode 100644
index 0000000..4cb441b
--- /dev/null
+++ b/src/Direction.java
@@ -0,0 +1,64 @@
+/**
+ * Enum for the direction of Thésée's movement.
+ * @version 1.0
+ * @author Amir Daouadi
+ * @author Lyanis Souidi
+ */
+public enum Direction {
+ /**
+ * Up direction
+ */
+ UP,
+
+ /**
+ * Down direction
+ */
+ DOWN,
+
+ /**
+ * Left direction
+ */
+ LEFT,
+
+ /**
+ * Right direction
+ */
+ RIGHT;
+
+ /**
+ * Get the index to add to the row of the current square to get the row of the next square.
+ * @return The index to add to the row
+ */
+ public int row() {
+ return switch (this) {
+ case UP -> -1;
+ case DOWN -> 1;
+ default -> 0;
+ };
+ }
+
+ /**
+ * Get the index to add to the column of the current square to get the column of the next square.
+ * @return The index to add to the column
+ */
+ public int column() {
+ return switch (this) {
+ case LEFT -> -1;
+ case RIGHT -> 1;
+ default -> 0;
+ };
+ }
+
+ /**
+ * Get the opposite direction
+ * @return The opposite direction
+ */
+ public Direction opposite() {
+ return switch (this) {
+ case UP -> DOWN;
+ case DOWN -> UP;
+ case LEFT -> RIGHT;
+ case RIGHT -> LEFT;
+ };
+ }
+}
diff --git a/src/Editor.java b/src/Editor.java
index 3af53ea..57c3620 100644
--- a/src/Editor.java
+++ b/src/Editor.java
@@ -1,5 +1,5 @@
public class Editor {
- private Grid gridModel;
+ private final Grid gridModel;
public Editor(Grid gridModel) {
this.gridModel = gridModel;
diff --git a/src/EditorController.java b/src/EditorController.java
index 358525a..4a92a0f 100644
--- a/src/EditorController.java
+++ b/src/EditorController.java
@@ -1,76 +1,141 @@
import javax.swing.*;
+import javax.swing.filechooser.FileNameExtensionFilter;
import java.awt.*;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
+import java.io.File;
import java.util.Random;
public class EditorController extends GridController {
- private Editor model;
- private EditorView view;
+ private final Editor model;
+ private final EditorView view;
private enum Mode { DISABLED, WALL, THESEE, EXIT }
private Mode editMode = Mode.DISABLED;
- private Button editTheseeButton = new Button("Placer Joueur");
- private Button editExitButton = new Button("Placer Sortie");
- private Button editWallButton = new Button("Enlever/Ajouter Murs");
+ private boolean edited = false;
+ private final Button editTheseeButton = new Button("Modifier Thésée");
+ private final Button editExitButton = new Button("Modifier Sortie");
+ private final Button editWallButton = new Button("Modifier Murs");
+ private final Button exportButton = new Button("Exporter");
+ private final Button startButton = new Button("Démarrer");
public EditorController(Editor model, EditorView view) {
super(model.getGrid(), view);
this.model = model;
this.view = view;
+ this.view.window.setPageTitle(this.model.getGrid().getFile().getName());
+
this.view.addMouseListener(new MouseAdapter() {
- public void mouseClicked(MouseEvent e) {
+ public void mousePressed(MouseEvent e) {
edit(view.click(e));
}
});
JPanel buttons = new JPanel();
-
- editWallButton.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- if (editMode == Mode.DISABLED) {
- editWallButton.setText("Mode Dessin");
- setEditMode(Mode.WALL);
- } else {
- editWallButton.setText("Enlever/Ajouter Murs");
- setEditMode(Mode.DISABLED);
- }
- }
- });
- buttons.add(editWallButton);
-
- editTheseeButton.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- if (editMode == Mode.DISABLED) {
- editTheseeButton.setText("Mode Dessin");
- setEditMode(Mode.THESEE);
- } else {
- editTheseeButton.setText("Placer Joueur");
- setEditMode(Mode.DISABLED);
- }
- }
- });
- buttons.add(editTheseeButton);
- editExitButton.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- if (editMode == Mode.DISABLED) {
- editExitButton.setText("Mode Dessin");
- setEditMode(Mode.EXIT);
- } else {
- editExitButton.setText("Placer Sortie");
- setEditMode(Mode.DISABLED);
+ Button randomizeButton = new Button("Aléatoire");
+ randomizeButton.addActionListener(e -> {
+ random();
+ if (!edited) {
+ edited = true;
+ view.window.setPageTitle("*" + view.window.getPageTitle());
+ }
+ view.repaint();
+ });
+ buttons.add(randomizeButton);
+
+ this.editWallButton.addActionListener(e -> setEditMode(Mode.WALL));
+ buttons.add(editWallButton);
+
+ this.editTheseeButton.addActionListener(e -> setEditMode(Mode.THESEE));
+ buttons.add(this.editTheseeButton);
+
+ this.editExitButton.addActionListener(e -> setEditMode(Mode.EXIT));
+ buttons.add(this.editExitButton);
+
+ this.exportButton.addActionListener(e -> {
+ JFileChooser fileChooser = new JFileChooser();
+ fileChooser.setDialogTitle("Sélectionnez le fichier dans lequel vous souhaitez enregistrer votre grille");
+ fileChooser.setSelectedFile(model.getGrid().getFile());
+ fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
+ fileChooser.setMultiSelectionEnabled(false);
+ fileChooser.setAcceptAllFileFilterUsed(false);
+ fileChooser.setFileFilter(new FileNameExtensionFilter("Fichier labyrinthe (*.lab)", "lab"));
+ int choix = fileChooser.showSaveDialog(view);
+ if (choix == JFileChooser.APPROVE_OPTION) {
+ File file = fileChooser.getSelectedFile();
+ if (!file.toString().toLowerCase().endsWith(".lab")) {
+ file = new File(file + ".lab");
+ }
+ view.window.setPageTitle(file.getName());
+ model.getGrid().setFile(file);
+ try {
+ FileManager.exportGrid(model.getGrid());
+ } catch (Exception ex) {
+ JOptionPane.showMessageDialog(view, ex.getMessage(), "Erreur", JOptionPane.ERROR_MESSAGE);
}
}
});
- buttons.add(editExitButton);
+
+ buttons.add(this.exportButton);
+
+ this.startButton.addActionListener(e -> {
+ if (!this.model.getGrid().validate()) {
+ JOptionPane.showMessageDialog(view, "La grille n'est pas valide.\nAssurez-vous que la grille contienne Thésée ainsi qu'une sortie, et que Thésée peut aller jusqu'à la sortie.", "Erreur", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ String[] algoOptions = {"Aléatoire", "Déterministe"};
+ String[] viewOptions = {"Automatique", "Manuel"};
+ int algoChoice = JOptionPane.showOptionDialog(view, "Choisissez l'algorithme à utiliser :", "Choix de l'algorithme", JOptionPane.DEFAULT_OPTION, JOptionPane.PLAIN_MESSAGE, null, algoOptions, algoOptions[0]);
+
+ if (algoChoice == -1) {
+ JOptionPane.showMessageDialog(view, "Aucun choix n'a été fait.", "Attention", JOptionPane.WARNING_MESSAGE);
+ return;
+ }
+
+ int viewChoice = JOptionPane.showOptionDialog(view, "Choisissez l'affichage à utiliser :", "Choix de l'affichage", JOptionPane.DEFAULT_OPTION, JOptionPane.PLAIN_MESSAGE, null, viewOptions, viewOptions[0]);
+
+ if (viewChoice == -1) {
+ JOptionPane.showMessageDialog(view, "Aucun choix n'a été fait.", "Attention", JOptionPane.WARNING_MESSAGE);
+ return;
+ }
+
+ AlgoType algoType = algoChoice == 0 ? AlgoType.RANDOM : AlgoType.DETERMINISTIC;
+
+ if (viewChoice == 0) {
+ AutoSimulation autoSimulation = new AutoSimulation(model.getGrid(), algoType);
+ AutoSimulationView autoSimulationView = new AutoSimulationView(autoSimulation);
+ new AutoSimulationController(autoSimulationView, autoSimulation);
+ view.window.setContentPane(autoSimulationView);
+ } else {
+ ManualSimulation manualSimulation = new ManualSimulation(model.getGrid(), algoType);
+ ManualSimulationView manualSimulationView = new ManualSimulationView(view.window, manualSimulation);
+ new ManualSimulationController(manualSimulationView, manualSimulation);
+ view.window.setContentPane(manualSimulationView);
+ }
+ view.window.validate();
+ });
+
+ buttons.add(this.startButton);
+
+ boolean validGrid = this.model.getGrid().validate();
+ this.exportButton.setEnabled(validGrid);
+ this.startButton.setEnabled(validGrid);
+
this.view.add(buttons, BorderLayout.NORTH);
}
+
+ /**
+ * Process the click on a square
+ * @param square The square clicked
+ */
private void edit(Square square) {
if (square != null) {
+ if (!this.edited) {
+ this.edited = true;
+ this.view.window.setPageTitle("*" + this.view.window.getPageTitle());
+ }
if (this.editMode == Mode.WALL) {
if (square.isWall()) {
square.setEmpty();
@@ -81,22 +146,24 @@ public class EditorController extends GridController {
JOptionPane.showMessageDialog(this.view, ex.getMessage(), "Erreur", JOptionPane.ERROR_MESSAGE);
}
}
- this.view.repaint();
} else if (this.editMode == Mode.THESEE) {
try {
- this.model.getGrid().getThesee().setSquare(square);
+ this.model.getGrid().getThesee().setSquare(square, true);
} catch (Exception ex) {
JOptionPane.showMessageDialog(this.view, ex.getMessage(), "Erreur", JOptionPane.ERROR_MESSAGE);
}
- this.view.repaint();
} else if (this.editMode == Mode.EXIT) {
try {
square.setExit();
} catch (Exception ex) {
JOptionPane.showMessageDialog(this.view, ex.getMessage(), "Erreur", JOptionPane.ERROR_MESSAGE);
}
- this.view.repaint();
}
+
+ boolean validGrid = this.model.getGrid().validate();
+ this.exportButton.setEnabled(validGrid);
+ this.startButton.setEnabled(validGrid);
+ this.view.repaint();
}
}
@@ -106,15 +173,10 @@ public class EditorController extends GridController {
*/
private void setEditMode(Mode mode) {
this.editMode = mode;
- if (mode != Mode.DISABLED) {
- this.editTheseeButton.setEnabled(mode == Mode.THESEE);
- this.editExitButton.setEnabled(mode == Mode.EXIT);
- this.editWallButton.setEnabled(mode == Mode.WALL);
- } else {
- this.editTheseeButton.setEnabled(true);
- this.editExitButton.setEnabled(true);
- this.editWallButton.setEnabled(true);
- }
+
+ this.editTheseeButton.setEnabled(mode != Mode.THESEE);
+ this.editExitButton.setEnabled(mode != Mode.EXIT);
+ this.editWallButton.setEnabled(mode != Mode.WALL);
}
/**
@@ -125,16 +187,25 @@ public class EditorController extends GridController {
Random rand = new Random();
Grid gridModel = this.model.getGrid();
+ gridModel.empty();
+
gridModel.getSquare(rand.nextInt(gridModel.getSize()), rand.nextInt(gridModel.getSize())).setExit();
- gridModel.getThesee().setSquare(gridModel.getSquare(rand.nextInt(gridModel.getSize()), rand.nextInt(gridModel.getSize())));
+ try {
+ gridModel.getThesee().setSquare(gridModel.getSquare(rand.nextInt(gridModel.getSize()), rand.nextInt(gridModel.getSize())));
+ } catch (Exception ignored) {}
for (int i = 0; i < gridModel.getSize(); i++) {
for (int j = 0; j < gridModel.getSize(); j++) {
if (!gridModel.getSquare(i, j).isExit() && !gridModel.getSquare(i, j).isThesee() && rand.nextInt(3) == 0) gridModel.getSquare(i, j).setWall();
}
}
+
+ // If the grid is invalid, try again.
+ if (!gridModel.validate()) random();
+ this.exportButton.setEnabled(true);
+ this.startButton.setEnabled(true);
} catch (Exception e) {
- System.out.println(e.getMessage());
+ System.err.println(e.getMessage());
}
}
}
diff --git a/src/FileManager.java b/src/FileManager.java
index 604832a..79786fe 100644
--- a/src/FileManager.java
+++ b/src/FileManager.java
@@ -1,7 +1,7 @@
import java.io.*;
/**
* Class to manage file import/export
- * @version 1.1
+ * @version 1.0
* @author Amir Daouadi
* @author Lyanis Souidi
*/
@@ -18,7 +18,7 @@ public class FileManager {
FileInputStream fs = new FileInputStream(file);
DataInputStream ds = new DataInputStream(fs);
try {
- grid = new Grid(ds.read());
+ grid = new Grid(ds.read(), file);
grid.getThesee().setSquare(grid.getSquare(ds.read(), ds.read()));
grid.getSquare(ds.read(), ds.read()).setExit();
@@ -47,10 +47,11 @@ public class FileManager {
/**
* Export a grid to a file
* @param grid The grid to export
- * @param file The file to export to
* @throws Exception If an error occurs during the export
*/
- public static void exportGrid(Grid grid, File file) throws Exception {
+ public static void exportGrid(Grid grid) throws Exception {
+ if (!grid.validate()) throw new Exception("La grille n'est pas valide. Assurez-vous que la grille contient Thésée et la sortie ainsi que la sortie puisse être accessible depuis la position de Thésée.");
+ File file = grid.getFile();
try {
FileOutputStream fs = new FileOutputStream(file);
DataOutputStream ds = new DataOutputStream(fs);
@@ -64,16 +65,9 @@ public class FileManager {
ds.writeByte(theseeSquare.getColumn());
// Écriture de la position de la sortie
- for (int i = 0; i < grid.getSize(); i++) {
- for (int j = 0; j < grid.getSize(); j++) {
- Square square = grid.getSquare(i, j);
- if (square.isExit()) {
- ds.writeByte(square.getRow());
- ds.writeByte(square.getColumn());
- break;
- }
- }
- }
+ Square exitSquare = grid.getExit();
+ ds.writeByte(exitSquare.getRow());
+ ds.writeByte(exitSquare.getColumn());
// Écriture des murs
int bit = 0;
@@ -95,6 +89,7 @@ public class FileManager {
if (bit != 0) {
ds.writeByte(value);
}
+ ds.close();
} catch (Exception e) {
throw new Exception("Une erreur est survenue lors de l'écriture du fichier.");
}
diff --git a/src/Grid.java b/src/Grid.java
index f85faf0..d298928 100644
--- a/src/Grid.java
+++ b/src/Grid.java
@@ -1,8 +1,52 @@
+import java.io.File;
+import java.util.Stack;
+
+/**
+ * This class is used to store the grid's data
+ * @version 1.0
+ * @author Amir Daouadi
+ * @author Lyanis Souidi
+ */
public class Grid {
+ /**
+ * The grid's squares
+ */
private Square[][] squares;
+
+ /**
+ * Thésée
+ */
private Thesee thesee = new Thesee();
+ /**
+ * The grid's name
+ */
+ private File file;
+
+ /**
+ * Constructor
+ * @param size The size of the grid (number of squares on a row/column)
+ */
public Grid(int size) {
+ createGrid(size, new File("Sans titre"));
+ }
+
+ /**
+ * Constructor
+ * @param size The size of the grid (number of squares on a row/column)
+ * @param file The name of the grid
+ */
+ public Grid(int size, File file) {
+ createGrid(size, file);
+ }
+
+ /**
+ * Create the grid
+ * @param size The size of the grid (number of squares on a row/column)
+ * @param file The name of the grid
+ */
+ private void createGrid(int size, File file) {
+ this.file = file;
this.squares = new Square[size][size];
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
@@ -11,6 +55,37 @@ public class Grid {
}
}
+
+ /**
+ * Empty the grid
+ * Removes the exit, Thésée and walls
+ * @see Square#setEmpty()
+ */
+ public void empty() {
+ this.thesee = new Thesee();
+ for (Square[] row : this.squares) {
+ for (Square square : row) {
+ square.setEmpty();
+ }
+ }
+ }
+
+ /**
+ * Get the grid's name
+ * @return The grid's name
+ */
+ public File getFile() {
+ return this.file;
+ }
+
+ /**
+ * Set the grid's name
+ * @param file The grid's name
+ */
+ public void setFile(File file) {
+ this.file = file;
+ }
+
/**
* Get the grid's size (number of squares on a row/column)
* @return The grid's size
@@ -34,7 +109,102 @@ public class Grid {
}
}
+ /**
+ * Get Thésée
+ * @return Thésée
+ */
public Thesee getThesee() {
return this.thesee;
}
+
+ /**
+ * Checks if all the accessible squares have been visited
+ * @return true if all the accessible squares have been visited, false otherwise
+ */
+ public boolean isEnded() {
+ for (Square[] row : this.squares) {
+ for (Square square : row) {
+ if (square.isAccessible() && !square.isVisited()) return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Resets Thésée's position and all the squares' visited and accessible status
+ */
+ public void reset() {
+ this.thesee.reset();
+ for (Square[] row : this.squares) {
+ for (Square square : row) {
+ square.setVisited(false);
+ }
+ }
+ }
+
+ /**
+ * Get the exit square
+ * @return The exit square
+ */
+ public Square getExit() {
+ for (Square[] row : this.squares) {
+ for (Square square : row) {
+ if (square.isExit()) return square;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Validates the grid
+ * @return true if the grid can be solved, false otherwise
+ */
+ public boolean validate() {
+ reset();
+ Square theseeSquare = this.thesee.getSquare();
+ if (theseeSquare == null) return false;
+
+ Square exitSquare = getExit();
+ if (exitSquare == null) return false;
+
+ if (theseeSquare == exitSquare) return false;
+
+ discover(theseeSquare);
+
+ return exitSquare.isAccessible();
+ }
+
+ /**
+ * Discovers all the accessible squares from a given square
+ * @param square The square to start from
+ */
+ private void discover(Square square) {
+ Stack discoveredSquares = new Stack<>();
+ discoveredSquares.push(square);
+ while (!discoveredSquares.isEmpty()) {
+ Square discoveredSquare = discoveredSquares.pop();
+
+ discoveredSquare.setAccessible(true);
+ try {
+ Square upSquare = getSquare(discoveredSquare.getRow() - 1, discoveredSquare.getColumn());
+ if (!upSquare.isWall() && !upSquare.isAccessible()) discoveredSquares.push(upSquare);
+ } catch (Exception ignored) {}
+
+ try {
+ Square downSquare = getSquare(discoveredSquare.getRow() + 1, discoveredSquare.getColumn());
+ if (!downSquare.isWall() && !downSquare.isAccessible()) discoveredSquares.push(downSquare);
+ } catch (Exception ignored) {}
+
+ try {
+ Square leftSquare = getSquare(discoveredSquare.getRow(), discoveredSquare.getColumn() - 1);
+ if (!leftSquare.isWall() && !leftSquare.isAccessible()) discoveredSquares.push(leftSquare);
+ } catch (Exception ignored) {}
+
+ try {
+ Square rightSquare = getSquare(discoveredSquare.getRow(), discoveredSquare.getColumn() + 1);
+ if (!rightSquare.isWall() && !rightSquare.isAccessible()) discoveredSquares.push(rightSquare);
+
+ } catch (Exception ignored) {}
+ }
+ }
}
\ No newline at end of file
diff --git a/src/GridView.java b/src/GridView.java
index 4ab9adc..3930555 100644
--- a/src/GridView.java
+++ b/src/GridView.java
@@ -2,15 +2,13 @@ import javax.swing.*;
import java.awt.*;
public class GridView extends JPanel {
- public Window window;
+ public final Window window;
protected Grid model;
protected int gridSize;
protected int gridStartX;
protected int gridStartY;
protected int squareSize;
private Font font;
- private final String exit = "∩";
- private final String thesee = "Θ";
/**
* Manages the display of the grid
@@ -44,7 +42,11 @@ public class GridView extends JPanel {
for (int i = 0; i < this.model.getSize(); i++) {
for (int j = 0; j < this.model.getSize(); j++) {
try {
- if (this.model.getSquare(i, j).isWall()) g.setColor(new Color(122, 68, 25));
+ Square square = this.model.getSquare(i, j);
+
+ if (square.isWall() && square.isVisited()) g.setColor(new Color(82, 79, 47));
+ else if (square.isWall()) g.setColor(new Color(122, 68, 25));
+ else if (square.isVisited()) g.setColor(new Color(41, 90, 69));
else g.setColor(new Color(77, 170, 87));
g.fillRect(this.gridStartX + (this.squareSize * j), this.gridStartY + (this.squareSize * i), this.squareSize, this.squareSize);
@@ -52,13 +54,15 @@ public class GridView extends JPanel {
FontMetrics metrics = g.getFontMetrics(this.font);
g.setFont(this.font);
// Draw exit
- if (this.model.getSquare(i, j).isExit()) {
- g.drawString(this.exit, (this.gridStartX + (this.squareSize * j)) + ((this.squareSize - metrics.stringWidth(this.exit)) / 2), (this.gridStartY + (this.squareSize * i)) + (((this.squareSize - metrics.getHeight()) / 2) + metrics.getAscent()));
+ if (square.isExit()) {
+ String exit = "∩";
+ g.drawString(exit, (this.gridStartX + (this.squareSize * j)) + ((this.squareSize - metrics.stringWidth(exit)) / 2), (this.gridStartY + (this.squareSize * i)) + (((this.squareSize - metrics.getHeight()) / 2) + metrics.getAscent()));
}
// Draw Thésée
- if (this.model.getThesee().getSquare() == this.model.getSquare(i, j)) {
- g.drawString(this.thesee, (this.gridStartX + (this.squareSize * j)) + ((this.squareSize - metrics.stringWidth(this.thesee)) / 2), (this.gridStartY + (this.squareSize * i)) + (((this.squareSize - metrics.getHeight()) / 2) + metrics.getAscent()));
+ if (this.model.getThesee().getSquare() == square) {
+ String thesee = "Θ";
+ g.drawString(thesee, (this.gridStartX + (this.squareSize * j)) + ((this.squareSize - metrics.stringWidth(thesee)) / 2), (this.gridStartY + (this.squareSize * i)) + (((this.squareSize - metrics.getHeight()) / 2) + metrics.getAscent()));
}
} catch (Exception e) {
System.out.println(e.getMessage());
diff --git a/src/HomeView.java b/src/HomeView.java
index f89a1f9..8cc635f 100644
--- a/src/HomeView.java
+++ b/src/HomeView.java
@@ -1,9 +1,10 @@
import javax.swing.*;
+import javax.swing.filechooser.FileNameExtensionFilter;
import java.awt.*;
import java.io.File;
public class HomeView extends JPanel {
- private Window window;
+ public final Window window;
public HomeView(Window window) {
this.window = window;
@@ -40,52 +41,50 @@ public class HomeView extends JPanel {
}
private Button choisirGrille() {
- JPanel panel = new JPanel();
Button choisirGrille = new Button("Générer une grille", new Dimension(250, 50));
choisirGrille.addActionListener(e -> {
- String strTaille = JOptionPane.showInputDialog(panel, "Entrez la taille de la grille :", "Taille de la grille", JOptionPane.PLAIN_MESSAGE);
+ String strTaille = JOptionPane.showInputDialog(this, "Entrez la taille de la grille :", "Taille de la grille", JOptionPane.PLAIN_MESSAGE);
if (strTaille != null && !strTaille.isEmpty()) {
if (!Character.isDigit(strTaille.charAt(0))) {
- JOptionPane.showMessageDialog(panel, "Le premier caractère doit être un chiffre ou nombre.", "Erreur", JOptionPane.ERROR_MESSAGE);
+ JOptionPane.showMessageDialog(this, "Le premier caractère doit être un chiffre ou nombre.", "Erreur", JOptionPane.ERROR_MESSAGE);
return;
}
try {
int taille = Integer.parseInt(strTaille);
- if (taille > 3 && taille < 21) {
+ if (taille >= 2 && taille <= 255) {
+ if (!sizeWarning(this, taille)) return;
+
String[] options = {"Remplir aléatoirement", "Partir d'une grille vide"};
- int choix = JOptionPane.showOptionDialog(panel, "Choisissez comment remplir la grille :", "Remplissage de la grille", JOptionPane.DEFAULT_OPTION, JOptionPane.PLAIN_MESSAGE, null, options, options[0]);
+ int choix = JOptionPane.showOptionDialog(this, "Choisissez comment remplir la grille :", "Remplissage de la grille", JOptionPane.DEFAULT_OPTION, JOptionPane.PLAIN_MESSAGE, null, options, options[0]);
EditorView editorView = new EditorView(window);
EditorController editorController = new EditorController(new Editor(new Grid(taille)), editorView);
switch (choix) {
- case 0:
+ case 0 -> {
// afficher la grille aléatoirement
editorController.random();
window.setContentPane(editorView);
window.validate();
- break;
- case 1:
+ }
+ case 1 -> {
window.setContentPane(editorView);
window.validate();
- break;
- default:
+ }
+ default ->
// gérer le cas où aucun choix n'a été fait
- JOptionPane.showMessageDialog(panel, "Aucun choix n'a été fait.", "Attention", JOptionPane.WARNING_MESSAGE);
- return;
+ JOptionPane.showMessageDialog(this, "Aucun choix n'a été fait.", "Attention", JOptionPane.WARNING_MESSAGE);
}
} else {
- String errorMessage = "La taille doit être au moins de 4.";
- if (taille >= 21) {
- errorMessage = "La taille ne doit pas dépasser 20.";
+ String errorMessage = "La taille doit être supérieur ou égale à 2.";
+ if (taille > 255) {
+ errorMessage = "La taille doit être inférieur ou égale à 255.";
}
- JOptionPane.showMessageDialog(panel, errorMessage, "Erreur", JOptionPane.ERROR_MESSAGE);
+ JOptionPane.showMessageDialog(this, errorMessage, "Erreur", JOptionPane.ERROR_MESSAGE);
}
} catch (NumberFormatException ex) {
- JOptionPane.showMessageDialog(panel, "Tapez " + strTaille.charAt(0) + " pour une grille " + strTaille.charAt(0) +"x"+ strTaille.charAt(0) +".", "Erreur", JOptionPane.ERROR_MESSAGE);
-
-
+ JOptionPane.showMessageDialog(this, "Tapez " + strTaille.charAt(0) + " pour une grille " + strTaille.charAt(0) +"x"+ strTaille.charAt(0) +".", "Erreur", JOptionPane.ERROR_MESSAGE);
}
}
});
@@ -94,22 +93,27 @@ public class HomeView extends JPanel {
}
private Button importerGrille() {
- JPanel panel = new JPanel();
Button importerGrille = new Button("Importer une grille", new Dimension(250, 50));
importerGrille.addActionListener(e -> {
JFileChooser fileChooser = new JFileChooser();
- fileChooser.setDialogTitle("Selectionnez le fichier ou se trouve votre grille");
- int choix = fileChooser.showOpenDialog(panel);
+ fileChooser.setDialogTitle("Sélectionnez le fichier où se trouve votre grille");
+ fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
+ fileChooser.setMultiSelectionEnabled(false);
+ fileChooser.setAcceptAllFileFilterUsed(false);
+ fileChooser.setFileFilter(new FileNameExtensionFilter("Fichier labyrinthe (*.lab)", "lab"));
+ int choix = fileChooser.showOpenDialog(this);
if (choix == JFileChooser.APPROVE_OPTION) {
File fichier = fileChooser.getSelectedFile();
try {
+ Grid grid = FileManager.importGrid(fichier);
+ if (!sizeWarning(this, grid.getSize())) return;
EditorView editorView = new EditorView(window);
- new EditorController(new Editor(FileManager.importGrid(fichier)), editorView);
+ new EditorController(new Editor(grid), editorView);
window.setContentPane(editorView);
window.validate();
} catch (Exception ex) {
- JOptionPane.showMessageDialog(panel, ex.getMessage(), "Erreur", JOptionPane.ERROR_MESSAGE);
+ JOptionPane.showMessageDialog(this, ex.getMessage(), "Erreur", JOptionPane.ERROR_MESSAGE);
}
}
});
@@ -117,6 +121,18 @@ public class HomeView extends JPanel {
return importerGrille;
}
+ /**
+ * Shows a warning message if the grid size is too big
+ * @param size the size of the grid
+ * @return true if the user wants to continue, false otherwise
+ */
+ public static boolean sizeWarning(JComponent parentComponent, int size) {
+ if (size <= 25) return true;
+ String[] options = {"Abandonner", "Continuer avec cette grille"};
+ int choice = JOptionPane.showOptionDialog(parentComponent, "Vous essayez d'ouvrir une grille de taille " + size + "x" + size + ".\nEn continuant avec cette grille, vous pourrez rencontrer des difficultées lors de l'edition et la durée des simulations de résolutions pourra être impactée.\nVoulez-vous continuer quand même ?", "Attention", JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE, null, options, options[0]);
+ return choice != 0;
+ }
+
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
diff --git a/src/Main.java b/src/Main.java
index 4f44671..4e2e87f 100644
--- a/src/Main.java
+++ b/src/Main.java
@@ -1,8 +1,30 @@
+import javax.swing.*;
+import java.io.File;
public class Main {
public static void main(String[] args) {
Window window = new Window();
- HomeView home = new HomeView(window);
- window.setContentPane(home);
+
+ // If the first argument is a .lab file, try to open it
+ if (args.length > 0 && args[0].toLowerCase().endsWith(".lab")) {
+ Grid grid = null;
+ try {
+ File file = new File(args[0]);
+ grid = FileManager.importGrid(new File(file.getAbsolutePath()));
+ } catch (Exception ignored) {}
+
+ if (grid != null && HomeView.sizeWarning(new JPanel(), grid.getSize())) {
+ window.setPageTitle(args[0]);
+ EditorView editorView = new EditorView(window);
+ new EditorController(new Editor(grid), editorView);
+ window.setContentPane(editorView);
+ window.setVisible(true);
+ return;
+ }
+ }
+
+ // Else, open the home page
+ HomeView homeView = new HomeView(window);
+ window.setContentPane(homeView);
window.setVisible(true);
}
}
\ No newline at end of file
diff --git a/src/ManualSimulation.java b/src/ManualSimulation.java
new file mode 100644
index 0000000..83c4263
--- /dev/null
+++ b/src/ManualSimulation.java
@@ -0,0 +1,57 @@
+/**
+ * This class is used to store the data of a manual simulation
+ * @version 1.0
+ * @author Amir Daouadi
+ * @author Lyanis Souidi
+ */
+public class ManualSimulation {
+ /**
+ * The grid to use for the simulations
+ */
+ private final Grid grid;
+
+ /**
+ * The algorithm type to use for the simulations
+ */
+ private final AlgoType algoType;
+
+ /**
+ * The simulation
+ */
+ private final Simulation simulation = new Simulation();
+
+ /**
+ * Constructor
+ * @param grid The grid to use for the simulation
+ * @param algoType The algorithm type to use for the simulation
+ */
+ public ManualSimulation(Grid grid, AlgoType algoType) {
+ this.grid = grid;
+ this.algoType = algoType;
+ }
+
+ /**
+ * Get the grid used for the simulation
+ * @return The grid
+ */
+ public Grid getGrid() {
+ return this.grid;
+ }
+
+ /**
+ * Get the algorithm type used for the simulation
+ * @return The algorithm type
+ */
+ public AlgoType getAlgoType() {
+ return this.algoType;
+ }
+
+ /**
+ * Get the simulation
+ * @return The simulation
+ */
+ public Simulation getSimulation() {
+ return this.simulation;
+ }
+
+}
diff --git a/src/ManualSimulationController.java b/src/ManualSimulationController.java
index 0944b69..ebc4b0f 100644
--- a/src/ManualSimulationController.java
+++ b/src/ManualSimulationController.java
@@ -1,7 +1,6 @@
import javax.swing.*;
import java.awt.*;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
+import java.awt.event.*;
/**
* Controller for the manual simulation view.
@@ -13,58 +12,68 @@ public class ManualSimulationController {
/**
* The simulation model
*/
- private Simulation model;
+ private final ManualSimulation model;
+
/**
* The manual simulation view
*/
- private ManualSimulationView view;
- /**
- * The grid model
- */
- private Grid grid;
+ private final ManualSimulationView view;
+
/**
* The algorithm used for the simulation
*/
private Algo algo;
- /**
- * The restart button
- */
- private Button restartButton = new Button("Recommencer");
+
/**
* The next button
*/
- private Button nextButton = new Button("Coup suivant");
+ private final Button nextButton = new Button("Coup suivant");
/**
* Constructor
- * @param model The simulation model
+ * @param model The manual simulation model
* @param view The manual simulation view
- * @param algo The algorithm used for the simulation
*/
- public ManualSimulationController(Simulation model, ManualSimulationView view, Algo algo) {
+ public ManualSimulationController(ManualSimulationView view, ManualSimulation model) {
this.model = model;
this.view = view;
- this.algo = algo;
JPanel buttons = new JPanel();
- restartButton.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- algo.reset();
- }
- });
- buttons.add(restartButton);
-
- nextButton.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- algo.nextMove();
- if (model.isSuccess()) {
- nextButton.setEnabled(false);
- }
- }
- });
+ nextButton.addActionListener(e -> move());
buttons.add(nextButton);
this.view.add(buttons, BorderLayout.NORTH);
+
+ this.view.addKeyListener(new KeyAdapter() {
+ public void keyPressed(KeyEvent e) {
+ move();
+ }
+ });
+ this.view.setFocusable(true);
+ this.view.requestFocusInWindow();
+
+ run();
+ }
+
+ /**
+ * Run the simulation
+ */
+ public void run() {
+ if (this.model.getAlgoType() == AlgoType.RANDOM) {
+ this.algo = new RandomAlgo(this.model.getGrid(), this.model.getSimulation());
+ } else {
+ this.algo = new DeterministicAlgo(this.model.getGrid(), this.model.getSimulation());
+ }
+ }
+
+ private void move() {
+ if (model.getSimulation().isEnded()) return;
+ this.algo.nextMove();
+ this.view.repaint();
+ if (model.getSimulation().isEnded()) {
+ nextButton.setEnabled(false);
+ JOptionPane.showMessageDialog(view, "Partie terminée en " + model.getSimulation().getMoves() + " coups !", "Fin de partie", JOptionPane.INFORMATION_MESSAGE);
+ }
}
}
diff --git a/src/ManualSimulationView.java b/src/ManualSimulationView.java
index f4e25f0..26ff082 100644
--- a/src/ManualSimulationView.java
+++ b/src/ManualSimulationView.java
@@ -10,15 +10,17 @@ public class ManualSimulationView extends GridView {
/**
* The simulation model
*/
- private Simulation model;
+ private final ManualSimulation model;
/**
* Constructor
* @param window The window
* @param model The simulation model
*/
- public ManualSimulationView(Window window, Simulation model) {
+ public ManualSimulationView(Window window, ManualSimulation model) {
super(window);
+ this.model = model;
+ super.setGrid(this.model.getGrid());
}
@Override
@@ -27,9 +29,8 @@ public class ManualSimulationView extends GridView {
g.setColor(Color.BLACK);
g.setFont(new Font("Arial", Font.PLAIN, 20));
- FontMetrics metrics = g.getFontMetrics(g.getFont());
- String movesStr = "Coups : " + this.model.getMoves();
- g.drawString(movesStr, 5, 5);
+ String movesStr = "Coups : " + this.model.getSimulation().getMoves();
+ g.drawString(movesStr, 10, 20);
}
}
diff --git a/src/RandomAlgo.java b/src/RandomAlgo.java
new file mode 100644
index 0000000..c27f2de
--- /dev/null
+++ b/src/RandomAlgo.java
@@ -0,0 +1,59 @@
+import java.util.Random;
+
+/**
+ * Random algorithm
+ * @version 0.1
+ * @author Amir Daouadi
+ * @author Lyanis Souidi
+ */
+public class RandomAlgo implements Algo {
+ /**
+ * The grid model
+ */
+ private final Grid grid;
+
+ /**
+ * The simulation model
+ */
+ private final Simulation simulation;
+
+ /**
+ * The Thésée controller
+ */
+ private final TheseeController theseeController;
+
+ /**
+ * Random number generator
+ */
+ private final Random random = new Random();
+
+ /**
+ * Constructor
+ * @param grid The grid model
+ * @param simulation The simulation model
+ */
+ public RandomAlgo(Grid grid, Simulation simulation) {
+ this.grid = grid;
+ this.simulation = simulation;
+ this.theseeController = new TheseeController(this.grid.getThesee());
+ this.grid.getThesee().getSquare().setVisited(true);
+ }
+
+ /**
+ * Makes the next move of the algorithm
+ */
+ public void nextMove() {
+ if (this.simulation.isEnded()) return;
+ Direction[] availableDirection = this.theseeController.getAvailableDirections();
+
+ Direction randomDirection = availableDirection[this.random.nextInt(availableDirection.length)];
+
+ this.theseeController.move(randomDirection, this.simulation);
+
+ this.theseeController.getAvailableDirections();
+ if (!this.simulation.isEnded() && this.grid.isEnded()) {
+ this.simulation.setEnded();
+ }
+ }
+
+}
diff --git a/src/RandomDirection.java b/src/RandomDirection.java
deleted file mode 100644
index 455fe99..0000000
--- a/src/RandomDirection.java
+++ /dev/null
@@ -1,15 +0,0 @@
-import java.util.Random;
-
-public class RandomDirection {
- public static final int UP = 0;
- public static final int DOWN = 1;
- public static final int LEFT = 2;
- public static final int RIGHT = 3;
-
- public static int getRandomDirection() {
- Random rand = new Random();
- int direction = rand.nextInt(4);
-
- return direction;
- }
-}
\ No newline at end of file
diff --git a/src/Simulation.java b/src/Simulation.java
index 67e6bb6..e16996e 100644
--- a/src/Simulation.java
+++ b/src/Simulation.java
@@ -11,9 +11,9 @@ public class Simulation {
private int moves = 0;
/**
- * If the simulation has been successful or not
+ * If the simulation has ended or not
*/
- private boolean success = false;
+ private boolean end = false;
/**
* Get the number of moves of the simulation
@@ -24,11 +24,18 @@ public class Simulation {
}
/**
- * Get if the simulation has been successful or not
- * @return If the simulation has been successful or not
+ * Check if the simulation has ended or not
+ * @return If the simulation has ended or not
*/
- public boolean isSuccess() {
- return this.success;
+ public boolean isEnded() {
+ return this.end;
+ }
+
+ /**
+ * Set the simulation as ended
+ */
+ public void setEnded() {
+ this.end = true;
}
/**
@@ -38,11 +45,4 @@ public class Simulation {
this.moves++;
}
- /**
- * Set if the simulation has been successful or not
- * @param success If the simulation has been successful or not
- */
- public void setSuccess(boolean success) {
- this.success = success;
- }
}
diff --git a/src/Square.java b/src/Square.java
index 6fdf6ff..15df910 100644
--- a/src/Square.java
+++ b/src/Square.java
@@ -1,8 +1,10 @@
public class Square {
- public final int row;
- public final int column;
- public int type = 0;
- private Grid gridModel;
+ private final int row;
+ private final int column;
+ private int type = 0;
+ private boolean isVisited = false;
+ private boolean isAccessible = false;
+ private final Grid gridModel;
public Square(Grid gridModel, int row, int column) {
this.gridModel = gridModel;
@@ -26,14 +28,6 @@ public class Square {
return this.type == 2;
}
- /**
- * Checks if the current square is empty (not a wall and not an exit)
- * @return true if the current square is empty, false otherwise
- */
- public boolean isEmpty() {
- return this.type == 0;
- }
-
public boolean isThesee() {
return this.gridModel.getThesee().getSquare() == this;
}
@@ -52,16 +46,11 @@ public class Square {
*/
public void setExit() throws Exception {
if (this.gridModel.getThesee().getSquare() == this) throw new Exception("Vous ne pouvez pas placer la sortie sur la même case que Thésée. Déplacez d'abord Thésée puis réessayez.");
- for (int i = 0; i < this.gridModel.getSize(); i++) {
- for (int j = 0; j < this.gridModel.getSize(); j++) {
- try {
- if (this.gridModel.getSquare(i, j).isExit()) {
- this.gridModel.getSquare(i, j).setEmpty();
- }
- } catch (Exception e) {
- System.err.println(e.getMessage());
- }
- }
+
+ Square oldExit = this.gridModel.getExit();
+
+ if (oldExit != null) {
+ oldExit.setEmpty();
}
this.type = 2;
@@ -85,4 +74,20 @@ public class Square {
public Grid getGrid() {
return this.gridModel;
}
+
+ public boolean isVisited() {
+ return this.isVisited;
+ }
+
+ public void setVisited(boolean isVisited) {
+ this.isVisited = isVisited;
+ }
+
+ public boolean isAccessible() {
+ return this.isAccessible;
+ }
+
+ public void setAccessible(boolean isAccessible) {
+ this.isAccessible = isAccessible;
+ }
}
diff --git a/src/Thesee.java b/src/Thesee.java
index 3b382ea..5cf346b 100644
--- a/src/Thesee.java
+++ b/src/Thesee.java
@@ -1,18 +1,57 @@
+/**
+ * Represents Thésée in the labyrinth.
+ */
public class Thesee {
private Square square;
private Square intialSquare;
+ /**
+ * Moves Thésée to the given square.
+ * @param square The square to move Thesee to
+ * @throws Exception If the given square is a wall
+ */
public void setSquare(Square square) throws Exception {
- if (square.isExit()) throw new Exception("Vous ne pouvez pas placer Thesee sur la même case que la sortie. Déplacez d'abord la sortie puis réessayez.");
- square.setEmpty();
- this.square = square;
- if (this.intialSquare == null) this.intialSquare = this.square;
+ setSquare(square, false);
}
+ /**
+ * Moves Thésée to the given square.
+ * @param square The square to move Thesee to
+ * @param initialPosition Whether the square is the initial position
+ * @throws Exception If the given square is a wall
+ */
+ public void setSquare(Square square, boolean initialPosition) throws Exception {
+ if (square.isExit()) throw new Exception("Vous ne pouvez pas placer Thésée sur la même case que la sortie. Déplacez d'abord la sortie puis réessayez.");
+ square.setEmpty();
+ this.square = square;
+ if (initialPosition) {
+ this.intialSquare = this.square;
+ } else {
+ if (this.intialSquare == null) this.intialSquare = this.square;
+ }
+ }
+
+ /**
+ * Get the square where Thésée is.
+ * @return The square where Thésée is
+ */
public Square getSquare() {
return this.square;
}
+ /**
+ * Get a square next to Thésée.
+ * @param direction The direction to get the square from
+ * @return The requested square
+ * @throws Exception If the position is out of grid's bounds
+ */
+ public Square getSquare(Direction direction) throws Exception {
+ return this.square.getGrid().getSquare(this.square.getRow() + direction.row(), this.square.getColumn() + direction.column());
+ }
+
+ /**
+ * Resets Thésée's position to the initial square/
+ */
public void reset() {
this.square = this.intialSquare;
}
diff --git a/src/TheseeController.java b/src/TheseeController.java
index cf89680..e882a33 100644
--- a/src/TheseeController.java
+++ b/src/TheseeController.java
@@ -1,9 +1,5 @@
-import javax.swing.*;
-import java.awt.event.KeyEvent;
-import java.awt.event.KeyListener;
-
-public class TheseeController implements KeyListener {
- private Thesee model;
+public class TheseeController {
+ private final Thesee model;
private GridView gridView;
public TheseeController(Thesee model, GridView gridView) {
@@ -11,78 +7,71 @@ public class TheseeController implements KeyListener {
this.gridView = gridView;
}
- public boolean moveUp() {
+ public TheseeController(Thesee model) {
+ this.model = model;
+ }
+
+ /**
+ * Move Thésée in the given direction
+ * @param direction The direction to move Thésée to
+ * @param simulation The simulation model
+ * @return true if the move was successful, false otherwise.
+ */
+ public boolean move(Direction direction, Simulation simulation) {
+ simulation.addMove();
try {
- Square currentSquare = this.model.getSquare();
- Square newSquare = currentSquare.getGrid().getSquare(currentSquare.getRow(), currentSquare.getColumn() - 1);
- if (newSquare.isWall()) return false;
- this.model.setSquare(newSquare);
- this.gridView.repaint();
+ Square newSquare = this.model.getSquare(direction);
+ newSquare.setVisited(true);
+ // If the new square is a wall, add a move to simulate the rollback of the move
+ if (newSquare.isWall()) {
+ simulation.addMove();
+ if (this.gridView != null) this.gridView.repaint();
+ return false;
+ }
+ if (newSquare.isExit()) simulation.setEnded();
+ else this.model.setSquare(newSquare);
+ if (this.gridView != null) this.gridView.repaint();
return true;
} catch (Exception e) {
return false;
}
}
- public boolean moveDown() {
+ /**
+ * Get the available directions for the next move.
+ * It helps prevent Thésée from going out of the grid.
+ * @return The available directions
+ */
+ public Direction[] getAvailableDirections() {
+ Direction[] availableDirections = new Direction[4];
+ int availableDirectionsCount = 0;
+
try {
- Square currentSquare = this.model.getSquare();
- Square newSquare = currentSquare.getGrid().getSquare(currentSquare.getRow(), currentSquare.getColumn() + 1);
- if (newSquare.isWall()) return false;
- this.model.setSquare(newSquare);
- this.gridView.repaint();
- return true;
- } catch (Exception e) {
- return false;
- }
- }
+ this.model.getSquare(Direction.UP);
+ availableDirections[availableDirectionsCount] = Direction.UP;
+ availableDirectionsCount++;
+ } catch (Exception ignored) {}
- public boolean moveLeft() {
try {
- Square currentSquare = this.model.getSquare();
- Square newSquare = currentSquare.getGrid().getSquare(currentSquare.getRow() - 1, currentSquare.getColumn());
- if (newSquare.isWall()) return false;
- this.model.setSquare(newSquare);
- this.gridView.repaint();
- return true;
- } catch (Exception e) {
- return false;
- }
- }
+ this.model.getSquare(Direction.DOWN);
+ availableDirections[availableDirectionsCount] = Direction.DOWN;
+ availableDirectionsCount++;
+ } catch (Exception ignored) {}
- public boolean moveRight() {
try {
- Square currentSquare = this.model.getSquare();
- Square newSquare = currentSquare.getGrid().getSquare(currentSquare.getRow() + 1, currentSquare.getColumn());
- if (newSquare.isWall()) return false;
- this.model.setSquare(newSquare);
- this.gridView.repaint();
- return true;
- } catch (Exception e) {
- return false;
- }
- }
+ this.model.getSquare(Direction.LEFT);
+ availableDirections[availableDirectionsCount] = Direction.LEFT;
+ availableDirectionsCount++;
+ } catch (Exception ignored) {}
- @Override
- public void keyPressed(KeyEvent e) {
- int keyCode = e.getKeyCode();
- boolean moved;
- if (keyCode == KeyEvent.VK_UP) {
- moved = moveUp();
- } else if (keyCode == KeyEvent.VK_DOWN) {
- moved = moveDown();
- } else if (keyCode == KeyEvent.VK_LEFT) {
- moved = moveLeft();
- } else if (keyCode == KeyEvent.VK_RIGHT) {
- moved = moveRight();
- }
- }
+ try {
+ this.model.getSquare(Direction.RIGHT);
+ availableDirections[availableDirectionsCount] = Direction.RIGHT;
+ availableDirectionsCount++;
+ } catch (Exception ignored) {}
- @Override
- public void keyReleased(KeyEvent e) {
- }
-
- @Override
- public void keyTyped(KeyEvent e) {
+ Direction[] availableDirectionsTrimmed = new Direction[availableDirectionsCount];
+ System.arraycopy(availableDirections, 0, availableDirectionsTrimmed, 0, availableDirectionsCount);
+ return availableDirectionsTrimmed;
}
}
diff --git a/src/Window.java b/src/Window.java
index 0761e77..d927e70 100644
--- a/src/Window.java
+++ b/src/Window.java
@@ -3,13 +3,24 @@ import java.awt.Dimension;
import java.awt.Toolkit;
public class Window extends JFrame {
+ private static final String programTitle = "Labyrinthe";
+ private String pageTitle = "";
public Window() {
- super("Labyrinthe");
+ super(programTitle);
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
Dimension d = new Dimension(screenSize.width-150, screenSize.height-150);
this.setSize(d);
this.setLocationRelativeTo(null);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
- this.setMinimumSize(new Dimension(750, 750));
+ this.setMinimumSize(new Dimension(800, 850));
+ }
+
+ public String getPageTitle() {
+ return this.pageTitle;
+ }
+
+ public void setPageTitle(String title) {
+ this.pageTitle = title;
+ this.setTitle(this.pageTitle + " - " + Window.programTitle);
}
}
\ No newline at end of file