diff --git a/Readme b/Readme new file mode 100644 index 0000000..e69de29 diff --git a/src/backend/Cell.java b/src/backend/Cell.java index 5ddb971..777c03e 100644 --- a/src/backend/Cell.java +++ b/src/backend/Cell.java @@ -4,6 +4,8 @@ public class Cell { int posX; int posY; int state; + int timeOfLifeSinceCut = 0; + int GROWING_TIME = 50; public Cell(int x, int y, int lifeState) { posX = x; @@ -26,6 +28,16 @@ public class Cell { state = lifeState; } + public void updateCell() { + if (state == 0) { + timeOfLifeSinceCut++; + if (timeOfLifeSinceCut > GROWING_TIME) { + timeOfLifeSinceCut = 0; + state = 1; + } + } + } + public int getState() { return state; } diff --git a/src/backend/Sheep.java b/src/backend/Sheep.java index b05d141..e897ab7 100644 --- a/src/backend/Sheep.java +++ b/src/backend/Sheep.java @@ -12,6 +12,11 @@ public class Sheep extends Agent { int hunger; Random rand; + int height; + int width; + private final int REPRODUCTION_RADIUS = 1; // Radius within which sheep can reproduce + private final double REPRODUCTION_PROBABILITY = 0.1; // Probability of reproduction per turn + Sheep(int x,int y){ //first we call the constructor of the superClass(Animal) @@ -35,24 +40,55 @@ public class Sheep extends Agent { } else { hunger++; } + height = world.getHeight(); + width = world.getHeight(); + + // Check for reproduction + for (Agent neighbor : neighbors) { + if (neighbor instanceof Sheep && this.isInArea(neighbor.getX(), neighbor.getY(), REPRODUCTION_RADIUS)) { + if (rand.nextDouble() < REPRODUCTION_PROBABILITY) { + reproduce(world); + break; + } + } + } this.moveRandom(); return hunger>10; } + + private void reproduce(Simulator world) { + // Find a random adjacent cell + int newX = x + rand.nextInt(3) - 1; + int newY = y + rand.nextInt(3) - 1; + + // Ensure the new position is within bounds and empty + if (newX >= 0 && newX < world.getWidth() && newY >= 0 && newY < world.getHeight() && !world.getOccupied(newX, newY)) { + world.setAgent(newX, newY, new Sheep(newX, newY)); + } + } private void moveRandom() { int direction = rand.nextInt(4); + int nx = x; + int ny = y; + if(direction == 0) { - x+=1; + nx+=1; } if(direction == 1) { - y+=1; + ny+=1; } if(direction == 2) { - x-=1; + nx-=1; } if(direction == 3) { - y-=1; + ny-=1; } + // Ensure the sheep does not move out of bounds + if (nx >= 0 && nx < width && ny >= 0 && ny < height) { + x = nx; + y = ny; + } } diff --git a/src/backend/Simulator.java b/src/backend/Simulator.java index 4a4e53d..806e024 100644 --- a/src/backend/Simulator.java +++ b/src/backend/Simulator.java @@ -1,5 +1,6 @@ package backend; import java.util.ArrayList; +import java.util.Iterator; import windowInterface.MyInterface; @@ -7,13 +8,13 @@ public class Simulator extends Thread { private MyInterface mjf; - private final int COL_NUM = 100; - private final int LINE_NUM = 100; + private final int COL_NUM = 50; + private final int LINE_NUM = 50; private final int LIFE_TYPE_NUM = 4; //Conway Radius : 1 private final int LIFE_AREA_RADIUS = 1; //Animal Neighborhood Radius : 5 - private final int ANIMAL_AREA_RADIUS = 2; + private final int ANIMAL_AREA_RADIUS = 3; private ArrayList fieldSurviveValues; private ArrayList fieldBirthValues; @@ -103,17 +104,15 @@ 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( - agent.getX(), - agent.getY(), - ANIMAL_AREA_RADIUS); - if(!agent.liveTurn( - neighbors, - this)) { - agents.remove(agent); - } + //Modified because of cross-modifications to the arraylist and java doesn't like that much + for (int i = 0; i < agents.size(); i++) { + Agent agent = agents.get(i); + ArrayList neighbors = this.getNeighboringAnimals(agent.getX(), agent.getY(), ANIMAL_AREA_RADIUS); + + if (agent.liveTurn(neighbors, this)) { + agents.remove(i); + i--; // Adjust the index to account for the removed element + } } //then evolution of the field // TODO : apply game rule to all cells of the field @@ -131,7 +130,9 @@ public class Simulator extends Thread { * and the count is in the birth list, * then the cell becomes alive */ - world.makeStep(); + if (!clickActionFlag) + world.makeStep(); + else updateCells(); } /* @@ -152,7 +153,52 @@ public class Simulator extends Thread { * method called when clicking on a cell in the interface */ public void clickCell(int x, int y) { - world.clickCell(x, y);; + if (clickActionFlag) { + setAgent(x, y); + } else { + world.clickCell(x, y);; + } + } + + public void setAgent(int x, int y) { + int cellValue = getCell(x, y); // Assuming you have a method getCell(x, y) to retrieve the cell value + String agentType = null; + int indexOfAgent = 0; + for (int i = 0; i < agents.size(); i++) { + Agent agent = agents.get(i); + if (agent.getX() == x && agent.getY() == y) { + if (agent instanceof Sheep) { + agentType = "Sheep"; + } else { + agentType = "Wolf"; + } + indexOfAgent = i; + } + } + + // If the cell is empty (0), set it to 1 (food) + if (cellValue == 0 && agentType == null) { + setCell(x, y, 1); // Set the cell to indicate food + } else if (cellValue == 1) { // If the cell has food (1), replace it with a sheep + agents.add(new Sheep(x, y)); // Add a new Sheep object to the agents list + setCell(x, y, 0); + } else if (agentType == "Sheep") {//If the cell is occupied by a sheep, turn it into a wolf + agents.remove(indexOfAgent); + agents.add(new Wolf(x, y)); + } else if (agentType == "Wolf") {//If the cell is occupied by a wolf, loop back to cell = 0 + agents.remove(indexOfAgent); + setCell(x, y, 0); + } + } + + public void setAgent(int x, int y, Agent agent) { + agents.add(agent); + setCell(x, y, 0); // Clear the cell if it had food + } + + + public void removeAgent(Agent agent) { + agents.remove(agent); } /** @@ -164,6 +210,15 @@ public class Simulator extends Thread { public int getCell(int x, int y) { return world.getCell(x, y); } + + private void updateCells() { + for (int y = 0; y < getHeight(); y++) { + for (int x = 0; x < getWidth(); x++) { + world.updateCell(x, y); + } + } + } + /** * * @return list of Animals in simulated world @@ -171,6 +226,15 @@ public class Simulator extends Thread { public ArrayList getAnimals(){ return agents; } + public boolean getOccupied(int x, int y) { + for (Agent agent : agents) { + if (agent.getX() == x && agent.getY() == y) { + return true; + } + } + return false; + } + /** * selects Animals in a circular area of simulated world * @param x center @@ -188,7 +252,7 @@ public class Simulator extends Thread { } return inArea; } - + /** * set value of cell * @param x coord of cell @@ -205,8 +269,22 @@ public class Simulator extends Thread { * the simulated world in its present state */ public ArrayList getSaveState() { - //TODO : complete method with proper return - return null; + ArrayList lines = new ArrayList<>(); + + // Iterate over the world and construct the lines + for (int y = 0; y < LINE_NUM; y++) { + StringBuilder lineBuilder = new StringBuilder(); + for (int x = 0; x < COL_NUM; x++) { + lineBuilder.append(getCell(x, y)).append(";"); + } + // Remove the last semicolon + if (lineBuilder.length() > 0) { + lineBuilder.deleteCharAt(lineBuilder.length() - 1); + } + lines.add(lineBuilder.toString()); + } + + return lines; } /** * @@ -249,14 +327,6 @@ public class Simulator extends Thread { * to be alive in new state */ public void generateRandom(float chanceOfLife) { - //TODO : complete method - /* - * Advice : - * as you should probably have a separate class - * representing the field of cells... - * maybe just make a constructor in there - * and use it here - */ world.generateRandom(chanceOfLife); } @@ -318,15 +388,57 @@ public class Simulator extends Thread { } public ArrayList getAgentsSave() { - //TODO : Same idea as the other save method, but for agents - return null; + ArrayList lines = new ArrayList<>(); + + StringBuilder sheepCoordinates = new StringBuilder(); + StringBuilder wolfCoordinates = new StringBuilder(); + + for (Agent agent : agents) { + String coordinates = agent.getX() + "," + agent.getY(); + if (agent instanceof Sheep) { + if (sheepCoordinates.length() > 0) { + sheepCoordinates.append(";"); + } + sheepCoordinates.append(coordinates); + } else if (agent instanceof Wolf) { + if (wolfCoordinates.length() > 0) { + wolfCoordinates.append(";"); + } + wolfCoordinates.append(coordinates); + } + } + + lines.add(sheepCoordinates.toString()); + lines.add(wolfCoordinates.toString()); + + return lines; } public void loadAgents(ArrayList stringArray) { - //TODO : Same idea as other load methods, but for agent list - + agents.clear(); + + // Load sheeps + String sheepLine = stringArray.get(0); + String[] sheepCoords = sheepLine.split(";"); + for (String coord : sheepCoords) { + String[] xy = coord.split(","); + int x = Integer.parseInt(xy[0]); + int y = Integer.parseInt(xy[1]); + agents.add(new Sheep(x, y)); + } + + // Load wolves + String wolfLine = stringArray.get(1); + String[] wolfCoords = wolfLine.split(";"); + for (String coord : wolfCoords) { + String[] xy = coord.split(","); + int x = Integer.parseInt(xy[0]); + int y = Integer.parseInt(xy[1]); + agents.add(new Wolf(x, y)); + } } + /** * used by label in interface to show the active click action * @return String representation of click action diff --git a/src/backend/Wolf.java b/src/backend/Wolf.java new file mode 100644 index 0000000..a397e13 --- /dev/null +++ b/src/backend/Wolf.java @@ -0,0 +1,98 @@ +package backend; + +import java.awt.Color; +import java.util.ArrayList; +import java.util.Random; + +// example of basic animal. +// do not hesitate to make it more complex +// and DO add at least another species that interact with it +// for example wolves that eat Sheep +public class Wolf extends Agent { + + int hunger; + Random rand; + private final int REPRODUCTION_RADIUS = 1; // Radius within which sheep can reproduce + private final double REPRODUCTION_PROBABILITY = 0.2; // Probability of reproduction per turn + + Wolf(int x,int y){ + //first we call the constructor of the superClass(Animal) + //with the values we want. + // here we decide that a Sheep is initially white using this constructor + super(x,y,Color.red); + // we give our sheep a hunger value of zero at birth + hunger = 0; + //we initialize the random number generator we will use to move randomly + rand = new Random(); + } + + /** + * action of the animal + * it can interact with the cells or with other animals + * as you wish + */ + public boolean liveTurn(ArrayList neighbors, Simulator world) { + boolean ateSheep = false; + + // Check for neighboring sheep to eat + for (Agent neighbor : neighbors) { + if (neighbor instanceof Sheep) { + // Eat the sheep (remove from the list) + world.removeAgent(neighbor); + hunger = 0; // Reset hunger + ateSheep = true; + break; + } + } + + // If no sheep were eaten, increase hunger + if (!ateSheep) { + hunger++; + } + + + // Check for reproduction + for (Agent neighbor : neighbors) { + if (neighbor instanceof Wolf && this.isInArea(neighbor.getX(), neighbor.getY(), REPRODUCTION_RADIUS)) { + if (rand.nextDouble() < REPRODUCTION_PROBABILITY) { + reproduce(world); + break; + } + } + } + + // Move randomly + this.moveRandom(); + + return hunger>10; + } + + private void reproduce(Simulator world) { + // Find a random adjacent cell + int newX = x + rand.nextInt(3) - 1; + int newY = y + rand.nextInt(3) - 1; + + // Ensure the new position is within bounds and empty + if (newX >= 0 && newX < world.getWidth() && newY >= 0 && newY < world.getHeight() && world.getCell(newX, newY) == 0) { + world.setAgent(newX, newY, new Wolf(newX, newY)); + } + } + + private void moveRandom() { + int direction = rand.nextInt(4); + if(direction == 0) { + x+=1; + } + if(direction == 1) { + y+=1; + } + if(direction == 2) { + x-=1; + } + if(direction == 3) { + y-=1; + } + } + + +} diff --git a/src/backend/World.java b/src/backend/World.java index 1ba16ae..4fa0161 100644 --- a/src/backend/World.java +++ b/src/backend/World.java @@ -37,6 +37,10 @@ public class World { } } + public void updateCell(int x, int y) { + grid.getCell(x, y).updateCell(); + } + public void makeStep() { Grid nextGrid = new Grid(width,height);