diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..85cd180 Binary files /dev/null and b/.DS_Store differ diff --git a/Project_A05/.DS_Store b/Project_A05/.DS_Store new file mode 100644 index 0000000..1c585fe Binary files /dev/null and b/Project_A05/.DS_Store differ diff --git a/Project_A05/Agents/.DS_Store b/Project_A05/Agents/.DS_Store new file mode 100644 index 0000000..85dbf30 Binary files /dev/null and b/Project_A05/Agents/.DS_Store differ diff --git a/Project_A05/Agents/flock.csv b/Project_A05/Agents/flock.csv deleted file mode 100644 index 4c685d8..0000000 --- a/Project_A05/Agents/flock.csv +++ /dev/null @@ -1 +0,0 @@ -10,10;11,11;9,11;12,10 \ No newline at end of file diff --git a/Project_A05/Agents/sheeps.csv b/Project_A05/Agents/sheeps.csv new file mode 100644 index 0000000..41eefa5 --- /dev/null +++ b/Project_A05/Agents/sheeps.csv @@ -0,0 +1,8 @@ +8;12 +9;3 +78;5 +64;39 +29;41 +2;54 +55;87 +92;4 diff --git a/Project_A05/Rule/.DS_Store b/Project_A05/Rule/.DS_Store new file mode 100644 index 0000000..e4f579b Binary files /dev/null and b/Project_A05/Rule/.DS_Store differ diff --git a/Project_A05/Rule/Book 2.xlsx b/Project_A05/Rule/Book 2.xlsx new file mode 100644 index 0000000..840586f Binary files /dev/null and b/Project_A05/Rule/Book 2.xlsx differ diff --git a/Project_A05/Rule/Règles.csv b/Project_A05/Rule/Règles.csv new file mode 100644 index 0000000..d35631c --- /dev/null +++ b/Project_A05/Rule/Règles.csv @@ -0,0 +1,2 @@ +1;2;3;4;5 +3;;;; diff --git a/Project_A05/Rule/ZombieRule.csv b/Project_A05/Rule/ZombieRule.csv new file mode 100644 index 0000000..509d0f6 --- /dev/null +++ b/Project_A05/Rule/ZombieRule.csv @@ -0,0 +1,3 @@ +2;3 +3; +2; diff --git a/Project_A05/Rule/zombieRules.csv b/Project_A05/Rule/zombieRules.csv new file mode 100755 index 0000000..006b853 Binary files /dev/null and b/Project_A05/Rule/zombieRules.csv differ diff --git a/Project_A05/src/backend/Agent.java b/Project_A05/src/backend/Agent.java index 8b11e23..1790082 100644 --- a/Project_A05/src/backend/Agent.java +++ b/Project_A05/src/backend/Agent.java @@ -30,6 +30,8 @@ public abstract class Agent { return dist sheepSurviveValues; + private ArrayList sheepBirthValues; + + public AgentRules() { + sheepSurviveValues = new ArrayList<>(); + sheepBirthValues = new ArrayList<>(); + + sheepBirthValues.addAll(Arrays.asList(2)); // Sheeps are born with 2 or more neighbors + } + + public boolean shouldBirth(int numberOfNeighbors) { + return sheepBirthValues.contains(numberOfNeighbors); + } +} diff --git a/Project_A05/src/backend/Grid.java b/Project_A05/src/backend/Grid.java new file mode 100644 index 0000000..aac1f86 --- /dev/null +++ b/Project_A05/src/backend/Grid.java @@ -0,0 +1,85 @@ +package backend; + +import java.util.Random; + +public class Grid { + + + private final int COL_NUM = 100; + private final int LINE_NUM = 100; + private int[][] worldGrid; // The grid + + + + + // Constructor to create a grid ang give its size + public Grid() { + worldGrid = new int[COL_NUM][LINE_NUM]; + } + + // Method to initialize the grid with all zeros + public void iniGrid() { + for (int i = 0; i < COL_NUM; i++) { + for (int j = 0; j < LINE_NUM; j++) { + worldGrid[i][j] = 0; // Set each cell to 0 + } + } + } + + // Method to get the width of the grid + public int getWidth() { + return COL_NUM; + } + + // Method to get the height of the grid + public int getHeight() { + return LINE_NUM; + } + + // method to access a cell in the grid + public int getCell(int x, int y) { + return worldGrid[x][y]; // Get the value at cell (x, y) + } + // method to set the value of a cell in the grid + public void setCell(int x, int y, int val) { + + worldGrid[x][y] = val; // Set the value at cell (x, y) + } + + + + public int[][] getGrid() { + return worldGrid; + } + + public int countNeighbors(int x, int y) { + int count = 0; + + // Check all nine cells around the given cell + for (int i = -1; i <= 1; i++) { + for (int j = -1; j <= 1; j++) { + int nx = x + i; + int ny = y + j; + + // Skip checking the cell itself + if (i == 0 && j == 0) { + continue; + } + + // Count how many cells are alive around the given cell + if (nx >= 0 && nx < getWidth() && ny >= 0 && ny < getHeight()) { + if (getCell(nx,ny) == 1) { // 1 represents alive + count++; + } + } + } + } + + return count; + } + + + + + +} \ No newline at end of file diff --git a/Project_A05/src/backend/Rules.java b/Project_A05/src/backend/Rules.java new file mode 100644 index 0000000..6f9e01d --- /dev/null +++ b/Project_A05/src/backend/Rules.java @@ -0,0 +1,109 @@ +package backend; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Random; + +public class Rules { + private ArrayList fieldSurviveValues; + private ArrayList fieldBirthValues; + + private int zombieProbability; + Random rand; + + public int zombieProbability() { + return zombieProbability; + } + + + public Rules() { + // create lists of survival and birth rules + fieldSurviveValues = new ArrayList<>(); + fieldBirthValues = new ArrayList<>(); + + + + } + + // define if cell should die or survive + public boolean shouldSurvive(int numberOfNeighbors) { + return fieldSurviveValues.contains(numberOfNeighbors); + } + + // define if cell should birth + public boolean shouldBirth(int numberOfNeighbors) { + return fieldBirthValues.contains(numberOfNeighbors); + } + + // define if cell should become zombie + public boolean shouldBecomeZombie(int zombieProbability) { + this.rand = new Random(); + int zombie = rand.nextInt(99); + + return zombie < zombieProbability ; + } + + + //method to load rules from a file + public void loadRule(ArrayList lines) { + if(lines.size()<=0) { + System.out.println("empty rule file"); + return; + } + //DONE TODO : remove previous rule (=emptying lists) + fieldSurviveValues.clear(); + fieldBirthValues.clear(); + + String surviveLine = lines.get(0); + String birthLine = lines.get(1); + + String[] surviveElements = surviveLine.split(";"); + System.out.println("Survival rules are:"); + for(int x=0; x lines) { + String zombieLine = lines.get(2); + System.out.println("dying cells are becoming zombies"); + String[] zombieElement = zombieLine.split(";"); + for (int x = 0; x < zombieElement.length; x++) { + String elem = zombieElement[x]; + zombieProbability = Integer.parseInt(elem); + } + return zombieProbability; + + } + + + + +} + + + + diff --git a/Project_A05/src/backend/Sheep.java b/Project_A05/src/backend/Sheep.java index ece8a18..0ace505 100644 --- a/Project_A05/src/backend/Sheep.java +++ b/Project_A05/src/backend/Sheep.java @@ -11,6 +11,8 @@ import java.util.Random; public class Sheep extends Agent { int hunger; + boolean eaten; + boolean isEaten; Random rand; Sheep(int x,int y){ @@ -31,29 +33,64 @@ public class Sheep extends Agent { */ public boolean liveTurn(ArrayList neighbors, Simulator world) { if(world.getCell(x, y)==1) { - world.setCell(x, y, 0); + world.setCell(x, y, 0); //sheep eats green cell it's on } else { hunger++; } + + eaten = this.eatenSheep(neighbors, world); // true if sheep is eaten by wolf, false if not this.moveRandom(); - return hunger>10; + + // next steps will make a sheep live or die + + if (hunger<2) { // is sheep doesn't die of hunger + return !eaten ; // either it dies eaten or it lives + } + else + return hunger<2;// sheep dies of hunger } + + public boolean eatenSheep(ArrayList neighbors, Simulator world) { + isEaten = false; + + for (Agent agent : neighbors) { + if (agent instanceof Wolf) { + if (Math.abs(agent.getX() - this.x) <= 1 && Math.abs(agent.getY() - this.y) <= 1) { //sheep is eaten if wolf in 1 cell radius + + isEaten = true; // Indicate that a sheep has been eaten + + } + } + } + return isEaten; + } + + public boolean eatSheep() { + return isEaten; + } + + + + + private void moveRandom() { int direction = rand.nextInt(4); - if(direction == 0) { + if(direction == 0 && x<99) { x+=1; } - if(direction == 1) { + if(direction == 1 && y<99) { y+=1; } - if(direction == 2) { + if(direction == 2 && x>0) { x-=1; } - if(direction == 3) { + if(direction == 3 && y>0) { y-=1; } } + + } diff --git a/Project_A05/src/backend/Simulator.java b/Project_A05/src/backend/Simulator.java index 52b5f9f..83960d5 100644 --- a/Project_A05/src/backend/Simulator.java +++ b/Project_A05/src/backend/Simulator.java @@ -1,14 +1,17 @@ package backend; import java.util.ArrayList; - +import java.util.List; +import java.util.Random; +import java.util.Iterator; import windowInterface.MyInterface; + public class Simulator extends Thread { private MyInterface mjf; - private final int COL_NUM = 100; - private final int LINE_NUM = 100; + + // COL_NUM and LINE_NUM moved to Grid private final int LIFE_TYPE_NUM = 4; //Conway Radius : 1 private final int LIFE_AREA_RADIUS = 1; @@ -24,8 +27,13 @@ public class Simulator extends Thread { private boolean loopingBorder; private boolean clickActionFlag; private int loopDelay = 150; + private int stepCounter = 0; - //TODO : add missing attribute(s) + //DONETODO : add missing attribute(s) + private Grid worldGrid; + private Rules rules = new Rules(); + Random rand; + public Simulator(MyInterface mjfParam) { mjf = mjfParam; @@ -38,8 +46,11 @@ public class Simulator extends Thread { fieldBirthValues = new ArrayList(); fieldSurviveValues = new ArrayList(); - //TODO : add missing attribute initialization + //DONETODO : add missing attribute initialization + worldGrid = new Grid(); + worldGrid.iniGrid(); + //Default rule : Survive always, birth never @@ -48,15 +59,17 @@ public class Simulator extends Thread { } } + + public int getWidth() { - //TODO : replace with proper return - return 0; + //DONETODO : replace with proper return + return worldGrid.getWidth(); // the width is the number of columns } public int getHeight() { - //TODO : replace with proper return - return 0; + //DONETODO : replace with proper return + return worldGrid.getHeight(); // the height is the number of rows } //Should probably stay as is @@ -91,6 +104,7 @@ public class Simulator extends Thread { // only modify if sure of what you do // to modify agent behavior, see liveTurn method // in agent classes + /* for(Agent agent : agents) { ArrayList neighbors = this.getNeighboringAnimals( @@ -103,14 +117,141 @@ public class Simulator extends Thread { agents.remove(agent); } } + */ + + // The code above gave us an error, we replaced it with this one, using an iterator + Iterator iterator = agents.iterator(); + while (iterator.hasNext()) { + Agent agent = iterator.next(); + ArrayList neighbors = + this.getNeighboringAnimals(agent.getX(), agent.getY(), ANIMAL_AREA_RADIUS); + if (!agent.liveTurn(neighbors, this)) { + iterator.remove(); + } + } + + List newAgents = new ArrayList<>(); + + // reproduction of agents + + for (int x = 0; x < worldGrid.getWidth(); x++) { + for (int y = 0; y < worldGrid.getHeight(); y++) { + if (isCellEmpty(x, y) && countNeighboringSheep(x, y) == 2) { + newAgents.add(new Sheep(x, y)); // add sheep in cell that doesn't contain any agent if there are exactly 2 neighboring sheeps. + } + } + } + agents.addAll(newAgents); + //then evolution of the field - // TODO : apply game rule to all cells of the field + + + for (int x = 0; x < worldGrid.getWidth(); x++) { + + for (int y = 0; y < worldGrid.getHeight(); y++) { + + + int neighbor = worldGrid.countNeighbors(x, y); // Calculate number of neighbors + + // Determine if cell should die, survive or birth. + boolean survives = rules.shouldSurvive(neighbor); + boolean born = rules.shouldBirth(neighbor); + + // Determine if cell should become a zombie. + boolean zombie = rules.shouldBecomeZombie(rules.zombieProbability()); + + if (getCell(x, y) == 1 && !survives && zombie) { + setCell(x, y, 2); // Cell becomes zombie + + + } else if (getCell(x, y) == 1 && !survives) { + setCell(x, y, 0); // Cell dies + + } else if (getCell(x, y) == 0 && born) { + setCell(x, y, 1); // New cell is born + } else if (getCell(x,y)==2) { + + this.contaminateCell(x,y); + } + + } + } + + // Check if there are more than 20 sheeps + if (countSheep() > 20) { + addWolfRandomly(); // Add a wolf if there are more than 20 sheeps on the grid. + } + } + + // Method that add a Wolf to a random location + public void addWolfRandomly() { + Random rand = new Random(); + int x = rand.nextInt(getWidth()); // Assume getWidth() gives the grid width + int y = rand.nextInt(getHeight()); // Assume getHeight() gives the grid height + agents.add(new Wolf(x, y)); // Add a new Wolf to the list + } + + // Method that allows a zombie to randomly contaminate a cell around it + public void contaminateCell(int x, int y) { + rand = new Random(); + int direction = rand.nextInt(4); + if(direction == 0 && x<99) { + x+=1; + } + if(direction == 1 && y<99) { + y+=1; + } + if(direction == 2 && x>0) { + x-=1; + } + if(direction == 3 && y>0) { + y-=1; + } + int contaminationProbability = rand.nextInt(4); // zombie has a 25% change of contaminating a cell. + + if (getCell(x,y)==1 && contaminationProbability == 0) { + this.setCell(x, y,2); // cell becomes zombie if contaminated + + } + } + + + private boolean isCellEmpty(int x, int y) { + return agents.stream().noneMatch(agent -> agent.getX() == x && agent.getY() == y); + } + + // method that count the number of Neighboring Sheep to a precise cell. + private int countNeighboringSheep(int x, int y) { + int count = 0; + for (Agent agent : agents) { // go through whole list of agents + if (agent instanceof Sheep) { //only count sheeps + if (Math.abs(agent.getX() - x) <= 1 && Math.abs(agent.getY() - y) <= 1 && !(agent.getX() == x && agent.getY() == y)) {// in a radius of 1 case around the case, without counting the case itself + count++; + } + } + } + return count; + } + // Method that counts the total number of sheeps on the grid + public int countSheep() { + int sheepCount = 0; + for (Agent agent : agents) { + if (agent instanceof Sheep) { + sheepCount++; // Increment count if the agent is a Sheep + } + } + return sheepCount; + } + + // DONETODO : apply game rule to all cells of the field + //see method countNeighboring, makeStep and class Rules /* you should distribute this action in methods/classes * don't write everything here ! * * the idea is first to get the surrounding values - * then count how many are alive + * then count how many are alive : + * then check if that number is in the lists of rules * if the cell is alive * and the count is in the survive list, @@ -119,11 +260,15 @@ public class Simulator extends Thread { * and the count is in the birth list, * then the cell becomes alive */ + + + + + - - } + /* * leave this as is @@ -136,14 +281,25 @@ public class Simulator extends Thread { * method called when clicking pause button */ public void togglePause() { - // TODO : actually toggle the corresponding flag + // DONETODO : actually toggle the corresponding flag + pauseFlag = !pauseFlag; } /** * method called when clicking on a cell in the interface */ public void clickCell(int x, int y) { - //TODO : complete method + if (clickActionFlag) { + // Assuming different types of actions based on flag + // E.g., toggling cell state + int currentVal = worldGrid.getCell(x, y); + int newVal = 1 - currentVal; + setCell(x, y, newVal); // Toggle between 0 and 1 + + } else { + // Assuming adding an agent if not toggling + agents.add(new Sheep(x, y)); // Default health for new agents + } } /** @@ -153,9 +309,14 @@ public class Simulator extends Thread { * @return value of cell */ public int getCell(int x, int y) { - //TODO : complete method with proper return - return 0; + //DONETODO : complete method with proper return + return worldGrid.getCell(x,y); } + + + + + /** * * @return list of Animals in simulated world @@ -170,6 +331,7 @@ public class Simulator extends Thread { * @param radius * @return list of agents in area */ + public ArrayList getNeighboringAnimals(int x, int y, int radius){ ArrayList inArea = new ArrayList(); for(int i=0;i getSaveState() { - //TODO : complete method with proper return - return null; + //DONETODO : complete method with proper return + int[][] gridData = worldGrid.getGrid(); // Get the grid + ArrayList saveState = new ArrayList<>(); + + for (int i = 0; i < worldGrid.getHeight(); i++) { + StringBuilder sb = new StringBuilder(); + for (int j = 0; j < worldGrid.getWidth(); j++) { + sb.append(gridData[i][j]); + if (j < worldGrid.getWidth() - 1) { + sb.append(";"); // Append a semicolon after each cell except the last in the row + } + } + saveState.add(sb.toString()); // Add the string for this row to the list + } + + return saveState; } /** * * @param lines of file representing saved world state */ + public void loadSaveState(ArrayList lines) { /* * First some checks that the file is usable @@ -211,6 +395,7 @@ public class Simulator extends Thread { * "Guard clauses", as they guard the method * against unwanted inputs */ + if(lines.size()<=0) { return; } @@ -219,10 +404,12 @@ public class Simulator extends Thread { if(firstLineElements.length<=0) { return; } + /* * now we fill in the world * with the content of the file */ + for(int y =0; y getRule() { - //TODO : complete method with proper return - - return null; - } + ArrayList rules = new ArrayList<>(); + StringBuilder sbSurvive = new StringBuilder("Survive:"); + for (Integer val : fieldSurviveValues) { + sbSurvive.append(val).append(";"); + } + if (!fieldSurviveValues.isEmpty()) sbSurvive.deleteCharAt(sbSurvive.length() - 1); - public void loadRule(ArrayList lines) { - if(lines.size()<=0) { - System.out.println("empty rule file"); - return; - } - //TODO : remove previous rule (=emptying lists) - - - String surviveLine = lines.get(0); - String birthLine = lines.get(1); - String[] surviveElements = surviveLine.split(";"); - for(int x=0; x getAgentsSave() { - //TODO : Same idea as the other save method, but for agents - return null; + //load rules from file + + public void loadRule(ArrayList lines) { + rules.loadRule(lines); + if(lines.size()>=3) { + rules.loadZombieRule(lines) ; + + } } - public void loadAgents(ArrayList stringArray) { - //TODO : Same idea as other load methods, but for agent list + + //method that return a string of agents + + public ArrayList getAgentsSave() { + ArrayList agentStates = new ArrayList<>(); + for (Agent agent : agents) { + agentStates.add(agent.toString()); + } + return agentStates; + + } + + //method that allows to load a file containing agent coordinates. + + public void loadAgents(ArrayList lines) { + + + for (String line : lines) { + String[] coordinates = line.split(";"); + int x = Integer.parseInt(coordinates[0]); + int y = Integer.parseInt(coordinates[1]); + if (Integer.parseInt(coordinates[2])==0) { + agents.add(new Sheep(x, y)); + } + if (Integer.parseInt(coordinates[2])==1) { + agents.add(new Wolf(x, y)); + } + + } + } @@ -324,9 +543,14 @@ public class Simulator extends Thread { * @return String representation of click action */ public String clickActionName() { - // TODO : initially return "sheep" or "cell" - // depending on clickActionFlag - return ""; + // Check the state of clickActionFlag + // Assuming true means we're placing an agent (e.g., Sheep) + // and false means we're toggling the state of a cell + if (clickActionFlag) { + return "cell"; // When flag is true, clicking adds a sheep + } else { + return "sheep"; // When flag is false, clicking toggles cell state + } } } diff --git a/Project_A05/src/backend/Wolf.java b/Project_A05/src/backend/Wolf.java new file mode 100644 index 0000000..03eb29c --- /dev/null +++ b/Project_A05/src/backend/Wolf.java @@ -0,0 +1,73 @@ +package backend; + +import java.awt.Color; +import java.util.ArrayList; +import java.util.Random; + + + +public class Wolf extends Agent { + private int loneliness; // this variable will be used to track how many steps since wolf found a friend + ArrayList neighbors = new ArrayList<>(); + + + public Wolf(int x, int y) { + super(x, y,Color.black); + this.loneliness = 0; // Initialize loneliness to 0 + + + } + + + + + @Override + public boolean liveTurn(ArrayList neighbors, Simulator world) { + + + moveRandomly(world); + + loneliness = lonelyWolf(neighbors); + return loneliness < 5; // Wolf dies if he didn't find another neighboring agent for 5 steps + } + + public int lonelyWolf(ArrayList allAgents) { + // Check if there are no neighboring agents around + neighbors = findNeighbors(allAgents,1); + if (neighbors.isEmpty()) { + loneliness++; // Increase loneliness if there are no neighbors + } else { + loneliness = 0; // Reset loneliness if there are neighbors + } + return loneliness; + } + + //method to find every agent near the wolf + private ArrayList findNeighbors(ArrayList allAgents, int radius) { + + for (Agent agent : allAgents) { + if (agent != this && this.isInArea(agent.getX(), agent.getY(), radius)) { + neighbors.add(agent); + } + } + return neighbors; + } + + + + +//method that makes a wolf move randomly + private void moveRandomly(Simulator world) { + Random rand = new Random(); + // Move randomly by one cell in any direction (including staying in the same place) + int newX = Math.max(0, Math.min(world.getWidth() - 1, getX() + rand.nextInt(3) - 1)); + int newY = Math.max(0, Math.min(world.getHeight() - 1, getY() + rand.nextInt(3) - 1)); + setPosition(newX, newY); + } + +// set position of wolf + private void setPosition(int x, int y) { + this.x = x; + this.y = y; + } +} diff --git a/Project_A05/src/windowInterface/JPanelDraw.java b/Project_A05/src/windowInterface/JPanelDraw.java index a210363..880f2d2 100644 --- a/Project_A05/src/windowInterface/JPanelDraw.java +++ b/Project_A05/src/windowInterface/JPanelDraw.java @@ -43,12 +43,12 @@ public class JPanelDraw extends JPanel { @Override protected void paintComponent(Graphics g) { super.paintComponent(g); - this.setBackground(Color.black); + this.setBackground(Color.gray); if (mySimu != null) { // Draw Interface from state of simulator float cellWidth = (float)this.getWidth()/(float)mySimu.getWidth(); float cellHeight = (float)this.getHeight()/(float)mySimu.getHeight(); - g.setColor(Color.gray); + g.setColor(Color.black); for(int x=0; x