commit c9c98a14d367329e55ae8ae1f69b5d1c8aa2a5b7 Author: Utilisateur Date: Sun Jun 2 20:32:54 2024 +0200 Test diff --git a/OOP_A2_Project/.classpath b/OOP_A2_Project/.classpath new file mode 100644 index 0000000..2197894 --- /dev/null +++ b/OOP_A2_Project/.classpath @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/OOP_A2_Project/.gitignore b/OOP_A2_Project/.gitignore new file mode 100644 index 0000000..ae3c172 --- /dev/null +++ b/OOP_A2_Project/.gitignore @@ -0,0 +1 @@ +/bin/ diff --git a/OOP_A2_Project/.project b/OOP_A2_Project/.project new file mode 100644 index 0000000..3686749 --- /dev/null +++ b/OOP_A2_Project/.project @@ -0,0 +1,17 @@ + + + OOP_A2_Project + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/OOP_A2_Project/.settings/org.eclipse.jdt.core.prefs b/OOP_A2_Project/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..3fd6c25 --- /dev/null +++ b/OOP_A2_Project/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,12 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.release=enabled +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/OOP_A2_Project/src/backend/Agent.java b/OOP_A2_Project/src/backend/Agent.java new file mode 100644 index 0000000..017ba8e --- /dev/null +++ b/OOP_A2_Project/src/backend/Agent.java @@ -0,0 +1,35 @@ +package backend; + +import java.awt.Color; + +public abstract class Agent { + protected int x; + protected int y; + protected World world; + + public Agent(int x, int y, World world) { + this.x = x; + this.y = y; + this.world = world; + } + + public abstract void step(); + + public int getX() { + return x; + } + + public int getY() { + return y; + } + + public abstract Color getDisplayColor(); + + protected int[] getRandomAdjacentPosition() { + int newX = x + (int)(Math.random() * 3) - 1; // -1, 0, or 1 + int newY = y + (int)(Math.random() * 3) - 1; // -1, 0, or 1 + newX = Math.max(0, Math.min(world.getCols() - 1, newX)); + newY = Math.max(0, Math.min(world.getRows() - 1, newY)); + return new int[]{newX, newY}; + } +} diff --git a/OOP_A2_Project/src/backend/Cell.java b/OOP_A2_Project/src/backend/Cell.java new file mode 100644 index 0000000..8db2512 --- /dev/null +++ b/OOP_A2_Project/src/backend/Cell.java @@ -0,0 +1,21 @@ +package backend; + +public class Cell { + private boolean isAlive; + + public Cell() { + this.isAlive = false; + } + + public boolean isAlive() { + return isAlive; + } + + public void setAlive(boolean isAlive) { + this.isAlive = isAlive; + } + + public void toggle() { + this.isAlive = !this.isAlive; + } +} diff --git a/OOP_A2_Project/src/backend/Sheep.java b/OOP_A2_Project/src/backend/Sheep.java new file mode 100644 index 0000000..54a5a6c --- /dev/null +++ b/OOP_A2_Project/src/backend/Sheep.java @@ -0,0 +1,22 @@ +package backend; + +import java.awt.Color; + +public class Sheep extends Agent { + + public Sheep(int x, int y, World world) { + super(x, y, world); + } + + @Override + public void step() { + int[] newPos = getRandomAdjacentPosition(); + x = newPos[0]; + y = newPos[1]; + } + + @Override + public Color getDisplayColor() { + return Color.white; + } +} diff --git a/OOP_A2_Project/src/backend/Simulator.java b/OOP_A2_Project/src/backend/Simulator.java new file mode 100644 index 0000000..376f5bb --- /dev/null +++ b/OOP_A2_Project/src/backend/Simulator.java @@ -0,0 +1,88 @@ +package backend; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import windowInterface.MyInterface; + +public class Simulator extends Thread { + private MyInterface myInterface; + private World world; + private boolean running = false; + private boolean paused = false; + private int loopDelay = 500; + private int clickAction = 0; + + public Simulator(MyInterface myInterface, World world) { + this.myInterface = myInterface; + this.world = world; + } + + public void setWorld(World world) { + this.world = world; + myInterface.update(world.getStepCount()); // Update interface with new world's step count + myInterface.printMessage("Changed to new world."); // Inform the user about the world change + } + + public void run() { + running = true; + while (running) { + if (!paused) { + world.step(); + myInterface.update(world.getStepCount()); + } + try { + Thread.sleep(loopDelay); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + public void stopSimu() { + running = false; + } + + public void togglePause() { + paused = !paused; + } + + public boolean isRunning() { + return running; + } + + public int getWidth() { + return world.getCols(); + } + + public int getHeight() { + return world.getRows(); + } + + public int getCell(int x, int y) { + return world.isCellAlive(y, x) ? 1 : 0; + } + + public void clickCell(int x, int y) { + world.toggleCell(y, x); + } + + public void addAgent(Agent agent) { + world.addAgent(agent); + } + + public List getAnimals() { + return world.getAgents(); + } + + public void setLoopDelay(int loopDelay) { + this.loopDelay = loopDelay; + } + + public void setClickAction(int action) { + this.clickAction = action; + } + + public World getWorld() { + return world; + } +} diff --git a/OOP_A2_Project/src/backend/Wolf.java b/OOP_A2_Project/src/backend/Wolf.java new file mode 100644 index 0000000..1674a99 --- /dev/null +++ b/OOP_A2_Project/src/backend/Wolf.java @@ -0,0 +1,74 @@ +package backend; + +import java.awt.Color; +import java.util.List; +import javax.swing.JOptionPane; + +public class Wolf extends Agent { + + public Wolf(int x, int y, World world) { + super(x, y, world); + } + + @Override + public void step() { + Sheep closestSheep = findClosestSheep(); + if (closestSheep != null) { + moveTowards(closestSheep); + if (x == closestSheep.getX() && y == closestSheep.getY()) { + world.removeAgent(closestSheep); + showMessage("A wolf caught a sheep at (" + x + ", " + y + ")"); + } + } else { + int[] newPos = getRandomAdjacentPosition(); + x = newPos[0]; + y = newPos[1]; + } + } + + private Sheep findClosestSheep() { + List agents = world.getAgents(); + Sheep closestSheep = null; + double minDistance = Double.MAX_VALUE; + + for (Agent agent : agents) { + if (agent instanceof Sheep) { + double distance = Math.sqrt(Math.pow(agent.getX() - x, 2) + Math.pow(agent.getY() - y, 2)); + if (distance < minDistance) { + minDistance = distance; + closestSheep = (Sheep) agent; + } + } + } + return closestSheep; + } + + private void moveTowards(Sheep sheep) { + if (sheep.getX() > x) x++; + else if (sheep.getX() < x) x--; + + if (sheep.getY() > y) y++; + else if (sheep.getY() < y) y--; + } + + @Override + protected int[] getRandomAdjacentPosition() { + int newX = x + (int)(Math.random() * 3) - 1; // -1, 0, or 1 + int newY = y + (int)(Math.random() * 3) - 1; // -1, 0, or 1 + newX = Math.max(0, Math.min(world.getCols() - 1, newX)); + newY = Math.max(0, Math.min(world.getRows() - 1, newY)); + return new int[]{newX, newY}; + } + + @Override + public Color getDisplayColor() { + return Color.GRAY; + } + + private void showMessage(String message) { + // Create a new thread to show the message so it doesn't block the simulation + new Thread(() -> { + JOptionPane.showMessageDialog(null, message); + }).start(); + } +} diff --git a/OOP_A2_Project/src/backend/World.java b/OOP_A2_Project/src/backend/World.java new file mode 100644 index 0000000..73788f8 --- /dev/null +++ b/OOP_A2_Project/src/backend/World.java @@ -0,0 +1,182 @@ +package backend; + +import java.awt.Color; +import java.util.List; +import java.util.Random; +import java.util.concurrent.CopyOnWriteArrayList; + +public class World { + private int rows; + private int cols; + private Cell[][] grid; + private boolean looping; + private int stepCount = 0; + private List agents; + private Color backgroundColor; + +public World(int rows, int cols, boolean looping, Color backgroundColor) { + this.rows = rows; + this.cols = cols; + this.looping = looping; + this.grid = new Cell[rows][cols]; + this.agents = new CopyOnWriteArrayList<>(); + this.backgroundColor = backgroundColor; + + for (int i = 0; i < rows; i++) { + for (int j = 0; j < cols; j++) { + grid[i][j] = new Cell(); + } + } +} + + + private Color generateRandomColor() { + Random random = new Random(); + int black = random.nextInt(256); + int green = random.nextInt(256); + int blue = random.nextInt(256); + return new Color(black, green, blue); + } + + public int getRows() { + return rows; + } + + public int getCols() { + return cols; + } + + public boolean isLooping() { + return looping; + } + + public void setLooping(boolean looping) { + this.looping = looping; + } + + public int getStepCount() { + return stepCount; + } + + public void step() { + Cell[][] newGrid = new Cell[rows][cols]; + for (int i = 0; i < rows; i++) { + for (int j = 0; j < cols; j++) { + newGrid[i][j] = new Cell(); + int liveNeighbors = countLiveNeighbors(i, j); + if (grid[i][j].isAlive()) { + newGrid[i][j].setAlive(liveNeighbors == 2 || liveNeighbors == 3); + } else { + newGrid[i][j].setAlive(liveNeighbors == 3); + } + } + } + grid = newGrid; + stepCount++; + + for (Agent agent : agents) { + agent.step(); + } + + checkCollisions(); + } + + private int countLiveNeighbors(int row, int col) { + int count = 0; + for (int i = -1; i <= 1; i++) { + for (int j = -1; j <= 1; j++) { + if (i == 0 && j == 0) continue; + int r = row + i; + int c = col + j; + if (looping) { + r = (r + rows) % rows; + c = (c + cols) % cols; + } + if (r >= 0 && r < rows && c >= 0 && c < cols && grid[r][c].isAlive()) { + count++; + } + } + } + return count; + } + + public void toggleCell(int row, int col) { + grid[row][col].toggle(); + } + + public boolean isCellAlive(int row, int col) { + return grid[row][col].isAlive(); + } + + public void setRandomState(float chanceOfLife) { + Random rand = new Random(); + for (int i = 0; i < rows; i++) { + for (int j = 0; j < cols; j++) { + grid[i][j].setAlive(rand.nextFloat() < chanceOfLife); + } + } + } + + public void setState(int row, int col, boolean isAlive) { + grid[row][col].setAlive(isAlive); + } + + public void loadStateFromStrings(String[] stateStrings) { + for (int i = 0; i < stateStrings.length; i++) { + String[] cells = stateStrings[i].split(";"); + for (int j = 0; j < cells.length; j++) { + grid[i][j].setAlive(cells[j].equals("1")); + } + } + } + + public String[] getStateAsStrings() { + String[] stateStrings = new String[rows]; + for (int i = 0; i < rows; i++) { + StringBuilder sb = new StringBuilder(); + for (int j = 0; j < cols; j++) { + sb.append(grid[i][j].isAlive() ? "1" : "0").append(";"); + } + stateStrings[i] = sb.toString(); + } + return stateStrings; + } + + public boolean isInsideWorld(int x, int y) { + return (x >= 0 && x < cols && y >= 0 && y < rows); + } + + public void addAgent(Agent agent) { + agents.add(agent); + } + + public void removeAgent(Agent agent) { + agents.remove(agent); + } + + public List getAgents() { + return agents; + } + + private void checkCollisions() { + for (Agent agent1 : agents) { + if (agent1 instanceof Wolf) { + for (Agent agent2 : agents) { + if (agent2 instanceof Sheep && agent1.getX() == agent2.getX() && agent1.getY() == agent2.getY()) { + System.out.println("A wolf caught a sheep at (" + agent1.getX() + ", " + agent1.getY() + ")"); + agents.remove(agent2); + break; + } + } + } + } + } + + public Color getBackgroundColor() { + return backgroundColor; + } + + public void setBackgroundColor(Color backgroundColor) { + this.backgroundColor = backgroundColor; + } +} diff --git a/OOP_A2_Project/src/windowInterface/JPanelDraw.java b/OOP_A2_Project/src/windowInterface/JPanelDraw.java new file mode 100644 index 0000000..27d6208 --- /dev/null +++ b/OOP_A2_Project/src/windowInterface/JPanelDraw.java @@ -0,0 +1,93 @@ +package windowInterface; + +import java.awt.Color; +import java.awt.Graphics; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import javax.swing.JPanel; +import backend.Agent; +import backend.Sheep; +import backend.Wolf; +import backend.Simulator; +import backend.World; + +public class JPanelDraw extends JPanel { + private static final long serialVersionUID = 1L; + private Simulator mySimu; + private MyInterface interfaceGlobal; + + public JPanelDraw(MyInterface itf) { + super(); + mySimu = null; + interfaceGlobal = itf; + addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent me) { + if (mySimu == null) { + interfaceGlobal.instantiateSimu(); + } + int x = (me.getX() * mySimu.getWidth()) / getWidth(); + int y = (me.getY() * mySimu.getHeight()) / getHeight(); + if (interfaceGlobal.getClickAction() == 1) { + mySimu.addAgent(new Sheep(x, y, mySimu.getWorld())); + } else if (interfaceGlobal.getClickAction() == 2) { + mySimu.addAgent(new Wolf(x, y, mySimu.getWorld())); + } else { + mySimu.clickCell(x, y); + } + repaint(); + } + }); + } + + public void setSimu(Simulator simu) { + mySimu = simu; + } +@Override +protected void paintComponent(Graphics g) { + super.paintComponent(g); + if (mySimu != null) { + World currentWorld = mySimu.getWorld(); + Color backgroundColor = currentWorld.getBackgroundColor(); + this.setBackground(backgroundColor); + float cellWidth = (float) this.getWidth() / (float) mySimu.getWidth(); + float cellHeight = (float) this.getHeight() / (float) mySimu.getHeight(); + g.setColor(Color.gray); + for (int x = 0; x < mySimu.getWidth(); x++) { + int graphX = Math.round(x * cellWidth); + g.drawLine(graphX, 0, graphX, this.getHeight()); + } + for (int y = 0; y < mySimu.getHeight(); y++) { + int graphY = Math.round(y * cellHeight); + g.drawLine(0, graphY, this.getWidth(), graphY); + } + for (int x = 0; x < mySimu.getWidth(); x++) { + for (int y = 0; y < mySimu.getHeight(); y++) { + int cellContent = mySimu.getCell(x, y); + if (cellContent == 0) { + continue; + } + if (cellContent == 1) { + g.setColor(Color.green); + } + g.fillRect( + (int) Math.round(x * cellWidth), + (int) Math.round(y * cellHeight), + (int) Math.round(cellWidth), + (int) Math.round(cellHeight) + ); + } + } + for (Agent animal : mySimu.getAnimals()) { + g.setColor(animal.getDisplayColor()); + int animalSizeMultiplier = 2; // Adjust this value to change the size of the animals + int animalSize = Math.min((int) Math.round(Math.min(cellWidth, cellHeight) * animalSizeMultiplier), Math.min((int) Math.round(cellWidth), (int) Math.round(cellHeight))); + g.fillOval((int) Math.round(animal.getX() * cellWidth), + (int) Math.round(animal.getY() * cellHeight), + animalSize, + animalSize); + } + } +} + + +} diff --git a/OOP_A2_Project/src/windowInterface/MyInterface.java b/OOP_A2_Project/src/windowInterface/MyInterface.java new file mode 100644 index 0000000..b3caa7c --- /dev/null +++ b/OOP_A2_Project/src/windowInterface/MyInterface.java @@ -0,0 +1,172 @@ +package windowInterface; + +import javax.swing.*; +import backend.Simulator; +import backend.Sheep; +import backend.Wolf; +import backend.World; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class MyInterface extends JFrame { + private static final long serialVersionUID = 1L; + private JPanelDraw panelDraw; + private Simulator simulator; + private JButton startButton; + private JButton pauseButton; + private JButton stopButton; + private JButton addSheepButton; + private JButton addWolfButton; + private JButton changeWorldButton; + private JSlider speedSlider; + private JLabel stepCountLabel; + private JTextArea messageArea; + private int clickAction = 0; + private int currentWorldIndex = 0; + private World[] worlds; + + public MyInterface() { + setTitle("Game of Life Simulator"); + setSize(800, 600); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + setLayout(new BorderLayout()); + + // Initialize worlds + worlds = new World[7]; + worlds[0] = new World(50, 50, true, Color.BLACK); // World 1 + worlds[1] = new World(40, 40, true, Color.CYAN); // World 2 + worlds[2] = new World(60, 60, true, Color.ORANGE); // World 3 + worlds[3] = new World(70, 70, true, Color.MAGENTA); // World 4 + worlds[4] = new World(45, 45, true, Color.YELLOW); // World 5 + worlds[5] = new World(55, 55, true, Color.PINK); // World 6 + worlds[6] = new World(35, 35, true, Color.GREEN); // World 7 + + panelDraw = new JPanelDraw(this); + add(panelDraw, BorderLayout.CENTER); + + JPanel controlPanel = new JPanel(); + controlPanel.setLayout(new GridLayout(2, 1)); + + JPanel buttonPanel = new JPanel(); + startButton = new JButton("Start"); + pauseButton = new JButton("Pause"); + stopButton = new JButton("Stop"); + addSheepButton = new JButton("Add Sheep"); + addWolfButton = new JButton("Add Wolf"); + changeWorldButton = new JButton("Change World"); + + startButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (simulator != null && !simulator.isRunning()) { + simulator.start(); + } + } + }); + + pauseButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (simulator != null) { + simulator.togglePause(); + } + } + }); + + stopButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (simulator != null) { + simulator.stopSimu(); + } + } + }); + + addSheepButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + clickAction = 1; + } + }); + + addWolfButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + clickAction = 2; + } + }); + + changeWorldButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (simulator != null) { + currentWorldIndex = (currentWorldIndex + 1) % worlds.length; // Cycle through worlds + simulator.setWorld(worlds[currentWorldIndex]); // Set the next world + } + } + }); + + buttonPanel.add(startButton); + buttonPanel.add(pauseButton); + buttonPanel.add(stopButton); + buttonPanel.add(addSheepButton); + buttonPanel.add(addWolfButton); + buttonPanel.add(changeWorldButton); + + controlPanel.add(buttonPanel); + + JPanel sliderPanel = new JPanel(); + speedSlider = new JSlider(0, 1000, 500); + speedSlider.setMajorTickSpacing(100); + speedSlider.setPaintTicks(true); + speedSlider.setPaintLabels(true); + speedSlider.addChangeListener(e -> { + if (simulator != null) { + simulator.setLoopDelay(speedSlider.getValue()); + } + }); + sliderPanel.add(new JLabel("Speed:")); + sliderPanel.add(speedSlider); + + stepCountLabel = new JLabel("Step Count: 0"); + sliderPanel.add(stepCountLabel); + + messageArea = new JTextArea(5, 20); + messageArea.setEditable(false); + JScrollPane scrollPane = new JScrollPane(messageArea); + + controlPanel.add(sliderPanel); + + add(controlPanel, BorderLayout.SOUTH); + add(scrollPane, BorderLayout.EAST); + + instantiateSimu(); + } + + public void instantiateSimu() { + // Initialize with the first world + simulator = new Simulator(this, worlds[0]); + panelDraw.setSimu(simulator); + } + + public void update(int stepCount) { + stepCountLabel.setText("Step Count: " + stepCount); + panelDraw.repaint(); + } + + public void printMessage(String message) { + messageArea.append(message + "\n"); + } + + public int getClickAction() { + return clickAction; + } + + public static void main(String[] args) { + SwingUtilities.invokeLater(() -> { + MyInterface myInterface = new MyInterface(); + myInterface.setVisible(true); + }); + } +}