rebase ff

Merge conflicts
This commit is contained in:
Lyanis SOUIDI 2023-04-28 23:24:33 +02:00
parent 21e2bdb163
commit 0e01163567
Signed by: Lyanis SOUIDI
GPG Key ID: 251ADD56CFE6A854
29 changed files with 1051 additions and 441 deletions

7
.idea/.gitignore vendored
View File

@ -1,8 +1 @@
# Default ignored files
/shelf/
/workspace.xml /workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@ -1,124 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Palette2">
<group name="Swing">
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
</item>
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
</item>
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.svg" removable="false" auto-create-binding="false" can-attach-label="true">
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
</item>
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
<initial-values>
<property name="text" value="Button" />
</initial-values>
</item>
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="RadioButton" />
</initial-values>
</item>
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="CheckBox" />
</initial-values>
</item>
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
<initial-values>
<property name="text" value="Label" />
</initial-values>
</item>
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
</item>
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
</item>
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
<preferred-size width="-1" height="20" />
</default-constraints>
</item>
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
</item>
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
</item>
</group>
</component>
</project>

12
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,12 @@
{
"version": "0.2.0",
"configurations": [
{
"type": "java",
"name": "Labyrinthe",
"request": "launch",
"mainClass": "Main",
"projectName": "SAE21_2022_c8f95a52"
}
]
}

View File

@ -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 ### ### VARIABLES ###
JC = javac JC = javac
@ -21,7 +29,7 @@ $(OUTDIR)/Main.class : $(OFILES)
### REGLES OPTIONNELLES ### ### REGLES OPTIONNELLES ###
run : $(OUTDIR)/Main.class run : $(OUTDIR)/Main.class
${JVM} ${JVMFLAGS} -cp $(OUTDIR) Main ${JVM} ${JVMFLAGS} -cp $(OUTDIR) Main $(RUN_ARGS)
clean : clean :
-rm -rf $(OUTDIR) -rm -rf $(OUTDIR)

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4"> <module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true"> <component name="NewModuleRootManager">
<output url="file://$MODULE_DIR$/out" />
<exclude-output /> <exclude-output />
<content url="file://$MODULE_DIR$"> <content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />

View File

@ -10,8 +10,4 @@ public interface Algo {
*/ */
void nextMove(); void nextMove();
/**
* Resets the algorithm
*/
void reset();
} }

17
src/AlgoType.java Normal file
View File

@ -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,
}

98
src/AutoSimulation.java Normal file
View File

@ -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;
}
}

View File

@ -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();
}
}
}
}

View File

@ -2,64 +2,51 @@ import javax.swing.*;
import java.awt.*; 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 * Display directly the success rate and the average number of moves of the simulations
* @version 1.0 * @version 1.1
* @author Amir Daouadi * @author Amir Daouadi
* @author Lyanis Souidi * @author Lyanis Souidi
*/ */
public class AutoSimulationView extends JPanel { public class AutoSimulationView extends JPanel {
/** public final AutoSimulation model;
* 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;
/** /**
* Constructor * Constructor
* @param simulations The simulations to display * @param model The automatic simulation model
*/ */
public AutoSimulationView(Simulation[] simulations) { public AutoSimulationView(AutoSimulation model) {
super(); super();
this.simulations = simulations; this.model = model;
this.setOpaque(false); this.setOpaque(false);
this.setPreferredSize(new Dimension(700, 500)); 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 @Override
public void paintComponent(Graphics g) { public void paintComponent(Graphics g) {
super.paintComponent(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)); g.setFont(new Font("Arial", Font.PLAIN, 20));
FontMetrics metrics = g.getFontMetrics(g.getFont()); FontMetrics metrics = g.getFontMetrics(g.getFont());
String successStr = "Taux de réussite : " + this.success + "%"; int y = 0;
g.drawString(successStr, (getWidth() - metrics.stringWidth(successStr)) / 2, ((getHeight() - metrics.getHeight()) / 2 + metrics.getAscent()) - 50);
String movesStr = "Nombre de mouvements moyen : " + this.moves; int endedSimulations = this.model.getNumberOfEndedSimulations();
g.drawString(movesStr, (getWidth() - metrics.stringWidth(movesStr)) / 2, ((getHeight() - metrics.getHeight()) / 2 + metrics.getAscent()) + 50); 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);
} }
} }

View File

@ -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<Direction> 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();
}
}
}

64
src/Direction.java Normal file
View File

@ -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;
};
}
}

View File

@ -1,5 +1,5 @@
public class Editor { public class Editor {
private Grid gridModel; private final Grid gridModel;
public Editor(Grid gridModel) { public Editor(Grid gridModel) {
this.gridModel = gridModel; this.gridModel = gridModel;

View File

@ -1,76 +1,141 @@
import javax.swing.*; import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.awt.*; import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter; import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.io.File;
import java.util.Random; import java.util.Random;
public class EditorController extends GridController { public class EditorController extends GridController {
private Editor model; private final Editor model;
private EditorView view; private final EditorView view;
private enum Mode { DISABLED, WALL, THESEE, EXIT } private enum Mode { DISABLED, WALL, THESEE, EXIT }
private Mode editMode = Mode.DISABLED; private Mode editMode = Mode.DISABLED;
private Button editTheseeButton = new Button("Placer Joueur"); private boolean edited = false;
private Button editExitButton = new Button("Placer Sortie"); private final Button editTheseeButton = new Button("Modifier Thésée");
private Button editWallButton = new Button("Enlever/Ajouter Murs"); 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) { public EditorController(Editor model, EditorView view) {
super(model.getGrid(), view); super(model.getGrid(), view);
this.model = model; this.model = model;
this.view = view; this.view = view;
this.view.window.setPageTitle(this.model.getGrid().getFile().getName());
this.view.addMouseListener(new MouseAdapter() { this.view.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) { public void mousePressed(MouseEvent e) {
edit(view.click(e)); edit(view.click(e));
} }
}); });
JPanel buttons = new JPanel(); 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() { Button randomizeButton = new Button("Aléatoire");
public void actionPerformed(ActionEvent e) { randomizeButton.addActionListener(e -> {
if (editMode == Mode.DISABLED) { random();
editExitButton.setText("Mode Dessin"); if (!edited) {
setEditMode(Mode.EXIT); edited = true;
} else { view.window.setPageTitle("*" + view.window.getPageTitle());
editExitButton.setText("Placer Sortie"); }
setEditMode(Mode.DISABLED); 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); this.view.add(buttons, BorderLayout.NORTH);
} }
/**
* Process the click on a square
* @param square The square clicked
*/
private void edit(Square square) { private void edit(Square square) {
if (square != null) { if (square != null) {
if (!this.edited) {
this.edited = true;
this.view.window.setPageTitle("*" + this.view.window.getPageTitle());
}
if (this.editMode == Mode.WALL) { if (this.editMode == Mode.WALL) {
if (square.isWall()) { if (square.isWall()) {
square.setEmpty(); square.setEmpty();
@ -81,22 +146,24 @@ public class EditorController extends GridController {
JOptionPane.showMessageDialog(this.view, ex.getMessage(), "Erreur", JOptionPane.ERROR_MESSAGE); JOptionPane.showMessageDialog(this.view, ex.getMessage(), "Erreur", JOptionPane.ERROR_MESSAGE);
} }
} }
this.view.repaint();
} else if (this.editMode == Mode.THESEE) { } else if (this.editMode == Mode.THESEE) {
try { try {
this.model.getGrid().getThesee().setSquare(square); this.model.getGrid().getThesee().setSquare(square, true);
} catch (Exception ex) { } catch (Exception ex) {
JOptionPane.showMessageDialog(this.view, ex.getMessage(), "Erreur", JOptionPane.ERROR_MESSAGE); JOptionPane.showMessageDialog(this.view, ex.getMessage(), "Erreur", JOptionPane.ERROR_MESSAGE);
} }
this.view.repaint();
} else if (this.editMode == Mode.EXIT) { } else if (this.editMode == Mode.EXIT) {
try { try {
square.setExit(); square.setExit();
} catch (Exception ex) { } catch (Exception ex) {
JOptionPane.showMessageDialog(this.view, ex.getMessage(), "Erreur", JOptionPane.ERROR_MESSAGE); 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) { private void setEditMode(Mode mode) {
this.editMode = mode; this.editMode = mode;
if (mode != Mode.DISABLED) {
this.editTheseeButton.setEnabled(mode == Mode.THESEE); this.editTheseeButton.setEnabled(mode != Mode.THESEE);
this.editExitButton.setEnabled(mode == Mode.EXIT); this.editExitButton.setEnabled(mode != Mode.EXIT);
this.editWallButton.setEnabled(mode == Mode.WALL); this.editWallButton.setEnabled(mode != Mode.WALL);
} else {
this.editTheseeButton.setEnabled(true);
this.editExitButton.setEnabled(true);
this.editWallButton.setEnabled(true);
}
} }
/** /**
@ -125,16 +187,25 @@ public class EditorController extends GridController {
Random rand = new Random(); Random rand = new Random();
Grid gridModel = this.model.getGrid(); Grid gridModel = this.model.getGrid();
gridModel.empty();
gridModel.getSquare(rand.nextInt(gridModel.getSize()), rand.nextInt(gridModel.getSize())).setExit(); 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 i = 0; i < gridModel.getSize(); i++) {
for (int j = 0; j < gridModel.getSize(); j++) { 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 (!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) { } catch (Exception e) {
System.out.println(e.getMessage()); System.err.println(e.getMessage());
} }
} }
} }

View File

@ -1,7 +1,7 @@
import java.io.*; import java.io.*;
/** /**
* Class to manage file import/export * Class to manage file import/export
* @version 1.1 * @version 1.0
* @author Amir Daouadi * @author Amir Daouadi
* @author Lyanis Souidi * @author Lyanis Souidi
*/ */
@ -18,7 +18,7 @@ public class FileManager {
FileInputStream fs = new FileInputStream(file); FileInputStream fs = new FileInputStream(file);
DataInputStream ds = new DataInputStream(fs); DataInputStream ds = new DataInputStream(fs);
try { try {
grid = new Grid(ds.read()); grid = new Grid(ds.read(), file);
grid.getThesee().setSquare(grid.getSquare(ds.read(), ds.read())); grid.getThesee().setSquare(grid.getSquare(ds.read(), ds.read()));
grid.getSquare(ds.read(), ds.read()).setExit(); grid.getSquare(ds.read(), ds.read()).setExit();
@ -47,10 +47,11 @@ public class FileManager {
/** /**
* Export a grid to a file * Export a grid to a file
* @param grid The grid to export * @param grid The grid to export
* @param file The file to export to
* @throws Exception If an error occurs during the export * @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 { try {
FileOutputStream fs = new FileOutputStream(file); FileOutputStream fs = new FileOutputStream(file);
DataOutputStream ds = new DataOutputStream(fs); DataOutputStream ds = new DataOutputStream(fs);
@ -64,16 +65,9 @@ public class FileManager {
ds.writeByte(theseeSquare.getColumn()); ds.writeByte(theseeSquare.getColumn());
// Écriture de la position de la sortie // Écriture de la position de la sortie
for (int i = 0; i < grid.getSize(); i++) { Square exitSquare = grid.getExit();
for (int j = 0; j < grid.getSize(); j++) { ds.writeByte(exitSquare.getRow());
Square square = grid.getSquare(i, j); ds.writeByte(exitSquare.getColumn());
if (square.isExit()) {
ds.writeByte(square.getRow());
ds.writeByte(square.getColumn());
break;
}
}
}
// Écriture des murs // Écriture des murs
int bit = 0; int bit = 0;
@ -95,6 +89,7 @@ public class FileManager {
if (bit != 0) { if (bit != 0) {
ds.writeByte(value); ds.writeByte(value);
} }
ds.close();
} catch (Exception e) { } catch (Exception e) {
throw new Exception("Une erreur est survenue lors de l'écriture du fichier."); throw new Exception("Une erreur est survenue lors de l'écriture du fichier.");
} }

View File

@ -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 { public class Grid {
/**
* The grid's squares
*/
private Square[][] squares; private Square[][] squares;
/**
* Thésée
*/
private Thesee thesee = new Thesee(); 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) { 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]; this.squares = new Square[size][size];
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) { 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) * Get the grid's size (number of squares on a row/column)
* @return The grid's size * @return The grid's size
@ -34,7 +109,102 @@ public class Grid {
} }
} }
/**
* Get Thésée
* @return Thésée
*/
public Thesee getThesee() { public Thesee getThesee() {
return this.thesee; 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<Square> 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) {}
}
}
} }

View File

@ -2,15 +2,13 @@ import javax.swing.*;
import java.awt.*; import java.awt.*;
public class GridView extends JPanel { public class GridView extends JPanel {
public Window window; public final Window window;
protected Grid model; protected Grid model;
protected int gridSize; protected int gridSize;
protected int gridStartX; protected int gridStartX;
protected int gridStartY; protected int gridStartY;
protected int squareSize; protected int squareSize;
private Font font; private Font font;
private final String exit = "";
private final String thesee = "Θ";
/** /**
* Manages the display of the grid * 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 i = 0; i < this.model.getSize(); i++) {
for (int j = 0; j < this.model.getSize(); j++) { for (int j = 0; j < this.model.getSize(); j++) {
try { 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)); else g.setColor(new Color(77, 170, 87));
g.fillRect(this.gridStartX + (this.squareSize * j), this.gridStartY + (this.squareSize * i), this.squareSize, this.squareSize); 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); FontMetrics metrics = g.getFontMetrics(this.font);
g.setFont(this.font); g.setFont(this.font);
// Draw exit // Draw exit
if (this.model.getSquare(i, j).isExit()) { if (square.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())); 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 // Draw Thésée
if (this.model.getThesee().getSquare() == this.model.getSquare(i, j)) { if (this.model.getThesee().getSquare() == square) {
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())); 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) { } catch (Exception e) {
System.out.println(e.getMessage()); System.out.println(e.getMessage());

View File

@ -1,9 +1,10 @@
import javax.swing.*; import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.awt.*; import java.awt.*;
import java.io.File; import java.io.File;
public class HomeView extends JPanel { public class HomeView extends JPanel {
private Window window; public final Window window;
public HomeView(Window window) { public HomeView(Window window) {
this.window = window; this.window = window;
@ -40,52 +41,50 @@ public class HomeView extends JPanel {
} }
private Button choisirGrille() { private Button choisirGrille() {
JPanel panel = new JPanel();
Button choisirGrille = new Button("Générer une grille", new Dimension(250, 50)); Button choisirGrille = new Button("Générer une grille", new Dimension(250, 50));
choisirGrille.addActionListener(e -> { 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 (strTaille != null && !strTaille.isEmpty()) {
if (!Character.isDigit(strTaille.charAt(0))) { 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; return;
} }
try { try {
int taille = Integer.parseInt(strTaille); 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"}; 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); EditorView editorView = new EditorView(window);
EditorController editorController = new EditorController(new Editor(new Grid(taille)), editorView); EditorController editorController = new EditorController(new Editor(new Grid(taille)), editorView);
switch (choix) { switch (choix) {
case 0: case 0 -> {
// afficher la grille aléatoirement // afficher la grille aléatoirement
editorController.random(); editorController.random();
window.setContentPane(editorView); window.setContentPane(editorView);
window.validate(); window.validate();
break; }
case 1: case 1 -> {
window.setContentPane(editorView); window.setContentPane(editorView);
window.validate(); window.validate();
break; }
default: default ->
// gérer le cas aucun choix n'a été fait // gérer le cas aucun choix n'a été fait
JOptionPane.showMessageDialog(panel, "Aucun choix n'a été fait.", "Attention", JOptionPane.WARNING_MESSAGE); JOptionPane.showMessageDialog(this, "Aucun choix n'a été fait.", "Attention", JOptionPane.WARNING_MESSAGE);
return;
} }
} else { } else {
String errorMessage = "La taille doit être au moins de 4."; String errorMessage = "La taille doit être supérieur ou égale à 2.";
if (taille >= 21) { if (taille > 255) {
errorMessage = "La taille ne doit pas dépasser 20."; 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) { } 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() { private Button importerGrille() {
JPanel panel = new JPanel();
Button importerGrille = new Button("Importer une grille", new Dimension(250, 50)); Button importerGrille = new Button("Importer une grille", new Dimension(250, 50));
importerGrille.addActionListener(e -> { importerGrille.addActionListener(e -> {
JFileChooser fileChooser = new JFileChooser(); JFileChooser fileChooser = new JFileChooser();
fileChooser.setDialogTitle("Selectionnez le fichier ou se trouve votre grille"); fileChooser.setDialogTitle("Sélectionnez le fichier où se trouve votre grille");
int choix = fileChooser.showOpenDialog(panel); 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) { if (choix == JFileChooser.APPROVE_OPTION) {
File fichier = fileChooser.getSelectedFile(); File fichier = fileChooser.getSelectedFile();
try { try {
Grid grid = FileManager.importGrid(fichier);
if (!sizeWarning(this, grid.getSize())) return;
EditorView editorView = new EditorView(window); EditorView editorView = new EditorView(window);
new EditorController(new Editor(FileManager.importGrid(fichier)), editorView); new EditorController(new Editor(grid), editorView);
window.setContentPane(editorView); window.setContentPane(editorView);
window.validate(); window.validate();
} catch (Exception ex) { } 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; 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 @Override
public void paintComponent(Graphics g) { public void paintComponent(Graphics g) {
super.paintComponent(g); super.paintComponent(g);

View File

@ -1,8 +1,30 @@
import javax.swing.*;
import java.io.File;
public class Main { public class Main {
public static void main(String[] args) { public static void main(String[] args) {
Window window = new Window(); 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); window.setVisible(true);
} }
} }

57
src/ManualSimulation.java Normal file
View File

@ -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;
}
}

View File

@ -1,7 +1,6 @@
import javax.swing.*; import javax.swing.*;
import java.awt.*; import java.awt.*;
import java.awt.event.ActionEvent; import java.awt.event.*;
import java.awt.event.ActionListener;
/** /**
* Controller for the manual simulation view. * Controller for the manual simulation view.
@ -13,58 +12,68 @@ public class ManualSimulationController {
/** /**
* The simulation model * The simulation model
*/ */
private Simulation model; private final ManualSimulation model;
/** /**
* The manual simulation view * The manual simulation view
*/ */
private ManualSimulationView view; private final ManualSimulationView view;
/**
* The grid model
*/
private Grid grid;
/** /**
* The algorithm used for the simulation * The algorithm used for the simulation
*/ */
private Algo algo; private Algo algo;
/**
* The restart button
*/
private Button restartButton = new Button("Recommencer");
/** /**
* The next button * The next button
*/ */
private Button nextButton = new Button("Coup suivant"); private final Button nextButton = new Button("Coup suivant");
/** /**
* Constructor * Constructor
* @param model The simulation model * @param model The manual simulation model
* @param view The manual simulation view * @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.model = model;
this.view = view; this.view = view;
this.algo = algo;
JPanel buttons = new JPanel(); JPanel buttons = new JPanel();
restartButton.addActionListener(new ActionListener() { nextButton.addActionListener(e -> move());
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);
}
}
});
buttons.add(nextButton); buttons.add(nextButton);
this.view.add(buttons, BorderLayout.NORTH); 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);
}
} }
} }

View File

@ -10,15 +10,17 @@ public class ManualSimulationView extends GridView {
/** /**
* The simulation model * The simulation model
*/ */
private Simulation model; private final ManualSimulation model;
/** /**
* Constructor * Constructor
* @param window The window * @param window The window
* @param model The simulation model * @param model The simulation model
*/ */
public ManualSimulationView(Window window, Simulation model) { public ManualSimulationView(Window window, ManualSimulation model) {
super(window); super(window);
this.model = model;
super.setGrid(this.model.getGrid());
} }
@Override @Override
@ -27,9 +29,8 @@ public class ManualSimulationView extends GridView {
g.setColor(Color.BLACK); g.setColor(Color.BLACK);
g.setFont(new Font("Arial", Font.PLAIN, 20)); g.setFont(new Font("Arial", Font.PLAIN, 20));
FontMetrics metrics = g.getFontMetrics(g.getFont());
String movesStr = "Coups : " + this.model.getMoves(); String movesStr = "Coups : " + this.model.getSimulation().getMoves();
g.drawString(movesStr, 5, 5); g.drawString(movesStr, 10, 20);
} }
} }

59
src/RandomAlgo.java Normal file
View File

@ -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();
}
}
}

View File

@ -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;
}
}

View File

@ -11,9 +11,9 @@ public class Simulation {
private int moves = 0; 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 * Get the number of moves of the simulation
@ -24,11 +24,18 @@ public class Simulation {
} }
/** /**
* Get if the simulation has been successful or not * Check if the simulation has ended or not
* @return If the simulation has been successful or not * @return If the simulation has ended or not
*/ */
public boolean isSuccess() { public boolean isEnded() {
return this.success; return this.end;
}
/**
* Set the simulation as ended
*/
public void setEnded() {
this.end = true;
} }
/** /**
@ -38,11 +45,4 @@ public class Simulation {
this.moves++; 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;
}
} }

View File

@ -1,8 +1,10 @@
public class Square { public class Square {
public final int row; private final int row;
public final int column; private final int column;
public int type = 0; private int type = 0;
private Grid gridModel; private boolean isVisited = false;
private boolean isAccessible = false;
private final Grid gridModel;
public Square(Grid gridModel, int row, int column) { public Square(Grid gridModel, int row, int column) {
this.gridModel = gridModel; this.gridModel = gridModel;
@ -26,14 +28,6 @@ public class Square {
return this.type == 2; 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() { public boolean isThesee() {
return this.gridModel.getThesee().getSquare() == this; return this.gridModel.getThesee().getSquare() == this;
} }
@ -52,16 +46,11 @@ public class Square {
*/ */
public void setExit() throws Exception { 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."); 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++) { Square oldExit = this.gridModel.getExit();
try {
if (this.gridModel.getSquare(i, j).isExit()) { if (oldExit != null) {
this.gridModel.getSquare(i, j).setEmpty(); oldExit.setEmpty();
}
} catch (Exception e) {
System.err.println(e.getMessage());
}
}
} }
this.type = 2; this.type = 2;
@ -85,4 +74,20 @@ public class Square {
public Grid getGrid() { public Grid getGrid() {
return this.gridModel; 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;
}
} }

View File

@ -1,18 +1,57 @@
/**
* Represents Thésée in the labyrinth.
*/
public class Thesee { public class Thesee {
private Square square; private Square square;
private Square intialSquare; 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 { 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."); setSquare(square, false);
square.setEmpty();
this.square = square;
if (this.intialSquare == null) this.intialSquare = this.square;
} }
/**
* 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() { public Square getSquare() {
return this.square; 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() { public void reset() {
this.square = this.intialSquare; this.square = this.intialSquare;
} }

View File

@ -1,9 +1,5 @@
import javax.swing.*; public class TheseeController {
import java.awt.event.KeyEvent; private final Thesee model;
import java.awt.event.KeyListener;
public class TheseeController implements KeyListener {
private Thesee model;
private GridView gridView; private GridView gridView;
public TheseeController(Thesee model, GridView gridView) { public TheseeController(Thesee model, GridView gridView) {
@ -11,78 +7,71 @@ public class TheseeController implements KeyListener {
this.gridView = gridView; 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 { try {
Square currentSquare = this.model.getSquare(); Square newSquare = this.model.getSquare(direction);
Square newSquare = currentSquare.getGrid().getSquare(currentSquare.getRow(), currentSquare.getColumn() - 1); newSquare.setVisited(true);
if (newSquare.isWall()) return false; // If the new square is a wall, add a move to simulate the rollback of the move
this.model.setSquare(newSquare); if (newSquare.isWall()) {
this.gridView.repaint(); 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; return true;
} catch (Exception e) { } catch (Exception e) {
return false; 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 { try {
Square currentSquare = this.model.getSquare(); this.model.getSquare(Direction.UP);
Square newSquare = currentSquare.getGrid().getSquare(currentSquare.getRow(), currentSquare.getColumn() + 1); availableDirections[availableDirectionsCount] = Direction.UP;
if (newSquare.isWall()) return false; availableDirectionsCount++;
this.model.setSquare(newSquare); } catch (Exception ignored) {}
this.gridView.repaint();
return true;
} catch (Exception e) {
return false;
}
}
public boolean moveLeft() {
try { try {
Square currentSquare = this.model.getSquare(); this.model.getSquare(Direction.DOWN);
Square newSquare = currentSquare.getGrid().getSquare(currentSquare.getRow() - 1, currentSquare.getColumn()); availableDirections[availableDirectionsCount] = Direction.DOWN;
if (newSquare.isWall()) return false; availableDirectionsCount++;
this.model.setSquare(newSquare); } catch (Exception ignored) {}
this.gridView.repaint();
return true;
} catch (Exception e) {
return false;
}
}
public boolean moveRight() {
try { try {
Square currentSquare = this.model.getSquare(); this.model.getSquare(Direction.LEFT);
Square newSquare = currentSquare.getGrid().getSquare(currentSquare.getRow() + 1, currentSquare.getColumn()); availableDirections[availableDirectionsCount] = Direction.LEFT;
if (newSquare.isWall()) return false; availableDirectionsCount++;
this.model.setSquare(newSquare); } catch (Exception ignored) {}
this.gridView.repaint();
return true;
} catch (Exception e) {
return false;
}
}
@Override try {
public void keyPressed(KeyEvent e) { this.model.getSquare(Direction.RIGHT);
int keyCode = e.getKeyCode(); availableDirections[availableDirectionsCount] = Direction.RIGHT;
boolean moved; availableDirectionsCount++;
if (keyCode == KeyEvent.VK_UP) { } catch (Exception ignored) {}
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();
}
}
@Override Direction[] availableDirectionsTrimmed = new Direction[availableDirectionsCount];
public void keyReleased(KeyEvent e) { System.arraycopy(availableDirections, 0, availableDirectionsTrimmed, 0, availableDirectionsCount);
} return availableDirectionsTrimmed;
@Override
public void keyTyped(KeyEvent e) {
} }
} }

View File

@ -3,13 +3,24 @@ import java.awt.Dimension;
import java.awt.Toolkit; import java.awt.Toolkit;
public class Window extends JFrame { public class Window extends JFrame {
private static final String programTitle = "Labyrinthe";
private String pageTitle = "";
public Window() { public Window() {
super("Labyrinthe"); super(programTitle);
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
Dimension d = new Dimension(screenSize.width-150, screenSize.height-150); Dimension d = new Dimension(screenSize.width-150, screenSize.height-150);
this.setSize(d); this.setSize(d);
this.setLocationRelativeTo(null); this.setLocationRelativeTo(null);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 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);
} }
} }