From 606ed35b2a1c94020970c86180d33f050e012cec Mon Sep 17 00:00:00 2001 From: Maxime Date: Fri, 31 May 2024 23:41:15 +0200 Subject: [PATCH] Final Version of the Game of Life Project --- src/GameOfLife.java | 6 -- src/backend/Sheep.java | 61 ++++++++++--- src/backend/Simulator.java | 174 ++++++++++++++++++++++++++----------- src/backend/Wolf.java | 94 ++++++++++++++++++++ src/git.java | 0 5 files changed, 262 insertions(+), 73 deletions(-) delete mode 100644 src/GameOfLife.java create mode 100644 src/backend/Wolf.java delete mode 100644 src/git.java diff --git a/src/GameOfLife.java b/src/GameOfLife.java deleted file mode 100644 index c151cc5..0000000 --- a/src/GameOfLife.java +++ /dev/null @@ -1,6 +0,0 @@ -public class GameOfLife { - - int cells[]; - - -} diff --git a/src/backend/Sheep.java b/src/backend/Sheep.java index 4242a32..f41b0bc 100644 --- a/src/backend/Sheep.java +++ b/src/backend/Sheep.java @@ -4,23 +4,25 @@ 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 +// Example of basic animal. + 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) - //with the values we want. - // here we decide that a Sheep is initially white using this constructor + // 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.white); - // we give our sheep a hunger value of zero at birth + // 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 + // We initialize the random number generator we will use to move randomly rand = new Random(); } @@ -35,25 +37,56 @@ 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 ux = x; + int uy = y; + if(direction == 0) { - x+=1; + ux+=1; } if(direction == 1) { - y+=1; + uy+=1; } if(direction == 2) { - x-=1; + ux-=1; } if(direction == 3) { - y-=1; + uy-=1; } + // Ensure the sheep does not move out of bounds + if (ux >= 0 && ux < width && uy >= 0 && uy < height) { + x = ux; + y = uy; + } } -} \ No newline at end of file +} diff --git a/src/backend/Simulator.java b/src/backend/Simulator.java index 5e989de..618f669 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; @@ -10,14 +11,15 @@ public class Simulator extends Thread { private final int COL_NUM = 100; private final int LINE_NUM = 100; private final int LIFE_TYPE_NUM = 4; - //Conway Radius : 1 + // Conway Radius : 1 private final int LIFE_AREA_RADIUS = 1; - //Animal Neighborhood Radius : 5 + // Animal Neighborhood Radius : 5 private final int ANIMAL_AREA_RADIUS = 2; private ArrayList fieldSurviveValues; private ArrayList fieldBirthValues; private ArrayList agents; + ArrayList> rule = new ArrayList<>(); private boolean stopFlag; private boolean pauseFlag; @@ -26,8 +28,6 @@ public class Simulator extends Thread { private int loopDelay = 150; Grid maingrid = new Grid(COL_NUM, LINE_NUM); - //TODO : add missing attribute(s) - public Simulator(MyInterface mjfParam) { mjf = mjfParam; stopFlag=false; @@ -89,20 +89,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); - } + 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 + // Then evolution of the field /* you should distribute this action in methods/classes * don't write everything here ! @@ -122,7 +117,7 @@ public class Simulator extends Thread { for (int y = 0; y < getHeight(); y++) { int actualState = getCell(x,y); int futureState =0; - // high life + // High life if(actualState==0) { for (int i = 1; i<=5; i++) { if(fieldBirthValues.contains(countNeighbors(x,y,i))) { @@ -175,14 +170,14 @@ public class Simulator extends Thread { } /* - * leave this as is + * Leave this as is */ public void stopSimu() { stopFlag=true; } /* - * method called when clicking pause button + * Method called when clicking pause button */ public void togglePause() { if (pauseFlag == true) { @@ -190,20 +185,58 @@ public class Simulator extends Thread { } else pauseFlag = true; } + + 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); + } /** - * method called when clicking on a cell in the interface + * Method called when clicking on a cell in the interface */ public void clickCell(int x, int y) { - //TODO : complete method - if(clickActionFlag) { - if (getCell(x, y)==0) { - setCell(x, y,1); - }else { - setCell(x, y,0); - } + if (clickActionFlag) { + setAgent(x, y); + } else { + setCell(x, y,0); } - } + } /** * get cell value in simulated world @@ -211,10 +244,10 @@ public class Simulator extends Thread { * @param y coordinate of cell * @return value of cell */ - public int getCell(int x, int y) { - //TODO : complete method with proper retur + public int getCell(int x, int y) { return maingrid.getValue(x, y); } + /** * * @return list of Animals in simulated world @@ -222,6 +255,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 @@ -247,7 +289,6 @@ public class Simulator extends Thread { * @param val to set in cell */ public void setCell(int x, int y, int val) { - //TODO : complete method maingrid.setValue(x, y, val); } @@ -257,7 +298,6 @@ public class Simulator extends Thread { * the simulated world in its present state */ public ArrayList getSaveState() { - //TODO : complete method with proper return ArrayList saveState = new ArrayList<>(); for (int y = 0; y < LINE_NUM; y++) { StringBuilder rowBuilder = new StringBuilder(); @@ -291,8 +331,7 @@ public class Simulator extends Thread { return; } /* - * now we fill in the world - * with the content of the file + * Now we fill in the world with the content of the file */ for(int y =0; y getRule() { - //TODO : complete method with proper return - StringBuilder birthValues = new StringBuilder(); StringBuilder surviveValues = new StringBuilder(); @@ -387,7 +419,7 @@ public class Simulator extends Thread { System.out.println("empty rule file"); return; } - //TODO : remove previous rule (=emptying lists) + fieldSurviveValues.clear(); fieldBirthValues.clear(); @@ -397,28 +429,65 @@ public class Simulator extends Thread { for(int x=0; x getAgentsSave() { - //TODO : Same idea as the other save method, but for agents - + ArrayList lines = new ArrayList<>(); - return null; + 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 Sheep + 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 Wolf + 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)); + } } /** @@ -426,9 +495,8 @@ public class Simulator extends Thread { * @return String representation of click action */ public String clickActionName() { - // TODO : initially return "sheep" or "cell" - // depending on clickActionFlag - if (clickActionFlag) return "cell"; else return "sheep"; + // Initially return "on" or "off" depending on clickActionFlag + if (clickActionFlag) return "on"; else return "off"; } } diff --git a/src/backend/Wolf.java b/src/backend/Wolf.java new file mode 100644 index 0000000..99bede2 --- /dev/null +++ b/src/backend/Wolf.java @@ -0,0 +1,94 @@ +package backend; + +import java.awt.Color; +import java.util.ArrayList; +import java.util.Random; + +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/git.java b/src/git.java deleted file mode 100644 index e69de29..0000000