diff --git a/src/backend/BasicAgent.java b/src/backend/BasicAgent.java new file mode 100644 index 0000000..57bf45f --- /dev/null +++ b/src/backend/BasicAgent.java @@ -0,0 +1,25 @@ +package backend; + +import java.awt.Color; +import java.util.ArrayList; + +public class BasicAgent extends Agent { + + public BasicAgent(int x, int y, Color color) { + super(x, y, color); + } + + @Override + public boolean liveTurn(ArrayList neighbors, Simulator world) { + // Example implementation: Move to a random neighboring position + int dx = (int) (Math.random() * 3) - 1; // random value -1, 0, or 1 + int dy = (int) (Math.random() * 3) - 1; // random value -1, 0, or 1 + + // Update position + this.x += dx; + this.y += dy; + + // Example condition for survival: always return true (agent never dies) + return true; + } +} diff --git a/src/backend/Sheep.java b/src/backend/Sheep.java index ece8a18..948f7d9 100644 --- a/src/backend/Sheep.java +++ b/src/backend/Sheep.java @@ -4,56 +4,109 @@ 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 Sheep extends Agent { - - int hunger; - Random rand; - - 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 - super(x,y,Color.white); - // 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(); - } + + int hunger; + Random rand; - /** - * action of the animal - * it can interact with the cells or with other animals - * as you wish - */ - public boolean liveTurn(ArrayList neighbors, Simulator world) { - if(world.getCell(x, y)==1) { - world.setCell(x, y, 0); - } else { - hunger++; - } - this.moveRandom(); - return hunger>10; - } - - 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; - } - } + Sheep(int x, int y) { + // Call the constructor of the superclass(Agent) with the specified values. + // Here we decide that a Sheep is initially white using this constructor. + super(x, y, Color.white); + // Initialize the hunger value of the sheep to zero at birth. + hunger = 0; + // Initialize the random number generator we will use to move randomly. + rand = new Random(); + } + /** + * Action of the sheep. + * It can interact with the cells or with other animals. + */ + public boolean liveTurn(ArrayList neighbors, Simulator world) { + // If the cell where the sheep is located is grass, eat it and reset hunger to zero. + if (world.getCell(x, y) == 1) { + world.setCell(x, y, 0); + hunger = 0; + } else { + // Increment hunger if no grass is found. + hunger++; + } + // Move the sheep randomly. + this.moveRandom(world); + // Return true if hunger is greater than 10, indicating the sheep dies. + return hunger > 10; + } + private void moveRandom(Simulator world) { + int direction = rand.nextInt(4); + // Move the sheep based on the random direction. + switch (direction) { + case 0: + x += 1; // Move right + break; + case 1: + y += 1; // Move down + break; + case 2: + x -= 1; // Move left + break; + case 3: + y -= 1; // Move up + break; + default: + break; + } + // Ensure the sheep stays within the bounds of the grid. + x = Math.max(0, Math.min(world.getWidth() - 1, x)); + y = Math.max(0, Math.min(world.getHeight() - 1, y)); + } +} + +class Wolf extends Agent { + + Random rand; + + Wolf(int x, int y) { + // Call the constructor of the superclass(Agent) with the specified values. + // Here we decide that a Wolf is initially gray using this constructor. + super(x, y, Color.gray); + // Initialize the random number generator we will use to move randomly. + rand = new Random(); + } + + /** + * Action of the wolf. + * It can interact with the cells or with other animals. + */ + public boolean liveTurn(ArrayList neighbors, Simulator world) { + // Move the wolf randomly. + this.moveRandom(world); + // Return true if the wolf always survives (example implementation). + return true; + } + + private void moveRandom(Simulator world) { + int direction = rand.nextInt(4); + // Move the wolf based on the random direction. + switch (direction) { + case 0: + x += 1; // Move right + break; + case 1: + y += 1; // Move down + break; + case 2: + x -= 1; // Move left + break; + case 3: + y -= 1; // Move up + break; + default: + break; + } + // Ensure the wolf stays within the bounds of the grid. + x = Math.max(0, Math.min(world.getWidth() - 1, x)); + y = Math.max(0, Math.min(world.getHeight() - 1, y)); + } } diff --git a/src/backend/Simulator.java b/src/backend/Simulator.java index 97328a1..825667c 100644 --- a/src/backend/Simulator.java +++ b/src/backend/Simulator.java @@ -1,335 +1,280 @@ package backend; + +import java.awt.Color; import java.util.ArrayList; import java.util.Iterator; +import java.util.Random; import windowInterface.MyInterface; public class Simulator extends Thread { - private MyInterface mjf; - - private final int COL_NUM = 100; - private final int LINE_NUM = 100; - 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 ArrayList fieldSurviveValues; - private ArrayList fieldBirthValues; - - private ArrayList agents; - - private boolean stopFlag; - private boolean pauseFlag; - private boolean loopingBorder; - private boolean clickActionFlag; - private int loopDelay = 150; + private MyInterface mjf; - //TODO : add missing attribute(s) + private final int COL_NUM = 100; + private final int LINE_NUM = 100; + 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 ArrayList fieldSurviveValues; + private ArrayList fieldBirthValues; - public Simulator(MyInterface mjfParam) { - mjf = mjfParam; - stopFlag=false; - pauseFlag=false; - loopingBorder=false; - clickActionFlag=false; + private ArrayList agents; - agents = new ArrayList(); - fieldBirthValues = new ArrayList(); - fieldSurviveValues = new ArrayList(); + private boolean stopFlag; + private boolean pauseFlag; + private boolean loopingBorder; + private boolean clickActionFlag; + private int loopDelay = 150; - //TODO : add missing attribute initialization - - - - //Default rule : Survive always, birth never - for(int i =0; i<9; i++) { - fieldSurviveValues.add(i); - } - - } + private int[][] grid; // Adding the grid to represent cells in the world - public int getWidth() { - //TODO : replace with proper return - return 0; - } + public Simulator(MyInterface mjfParam) { + mjf = mjfParam; + stopFlag = false; + pauseFlag = false; + loopingBorder = false; + clickActionFlag = false; - public int getHeight() { - //TODO : replace with proper return - return 0; - } + agents = new ArrayList(); + fieldBirthValues = new ArrayList(); + fieldSurviveValues = new ArrayList(); - //Should probably stay as is - public void run() { - int stepCount=0; - while(!stopFlag) { - stepCount++; - makeStep(); - mjf.update(stepCount); - try { - Thread.sleep(loopDelay); - } catch (InterruptedException e) { - e.printStackTrace(); - } - while(pauseFlag && !stopFlag) { - try { - Thread.sleep(loopDelay); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - } + grid = new int[LINE_NUM][COL_NUM]; // Initializing the grid - } + // Default rule : Survive always, birth never + for (int i = 0; i < 9; i++) { + fieldSurviveValues.add(i); + } + } - /** - * method called at each step of the simulation - * makes all the actions to go from one step to the other - */ - public void makeStep() { - // agent behaviors first - // only modify if sure of what you do - // to modify agent behavior, see liveTurn method - // in agent classes - Iterator agentIterator = agents.iterator(); - while(agentIterator.hasNext()) { - Agent agent = agentIterator.next(); - ArrayList neighbors = - this.getNeighboringAnimals( - agent.getX(), - agent.getY(), - ANIMAL_AREA_RADIUS); - if(!agent.liveTurn( - neighbors, - this)) { - agentIterator.remove(); - } - } - //then evolution of the field - // TODO : apply game rule to all cells of the field - - /* 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 check if that number is in the lists of rules - * if the cell is alive - * and the count is in the survive list, - * then the cell stays alive - * if the cell is not alive - * and the count is in the birth list, - * then the cell becomes alive - */ - - - - - } - - /* - * leave this as is - */ - public void stopSimu() { - stopFlag=true; - } - - /* - * method called when clicking pause button - */ - public void togglePause() { - // TODO : actually toggle the corresponding flag - } - - /** - * method called when clicking on a cell in the interface - */ - public void clickCell(int x, int y) { - //TODO : complete method - } - - /** - * get cell value in simulated world - * @param x coordinate of cell - * @param y coordinate of cell - * @return value of cell - */ - public int getCell(int x, int y) { - //TODO : complete method with proper return - return 0; - } - /** - * - * @return list of Animals in simulated world - */ - public ArrayList getAnimals(){ - return agents; - } - /** - * selects Animals in a circular area of simulated world - * @param x center - * @param y center - * @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; - } - /** - * - * @param lines of file representing saved world state - */ - public void loadSaveState(ArrayList lines) { - /* - * First some checks that the file is usable - * We call early returns in conditions like this - * "Guard clauses", as they guard the method - * against unwanted inputs - */ - if(lines.size()<=0) { - return; - } - String firstLine = lines.get(0); - String[] firstLineElements = firstLine.split(";"); - 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; - } + /** + * method called at each step of the simulation + * makes all the actions to go from one step to the other + */ + public void makeStep() { + // Agent behaviors first + Iterator agentIterator = agents.iterator(); + while (agentIterator.hasNext()) { + Agent agent = agentIterator.next(); + ArrayList neighbors = this.getNeighboringAnimals( + agent.getX(), + agent.getY(), + ANIMAL_AREA_RADIUS); + if (!agent.liveTurn(neighbors, this)) { + agentIterator.remove(); + } + } - 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; - } + // Then evolution of the field + int[][] newGrid = new int[LINE_NUM][COL_NUM]; - public void loadAgents(ArrayList stringArray) { - //TODO : Same idea as other load methods, but for agent list - - } + for (int y = 0; y < LINE_NUM; y++) { + for (int x = 0; x < COL_NUM; x++) { + int aliveNeighbors = countAliveNeighbors(x, y); + if (grid[y][x] == 1) { + newGrid[y][x] = fieldSurviveValues.contains(aliveNeighbors) ? 1 : 0; + } else { + newGrid[y][x] = fieldBirthValues.contains(aliveNeighbors) ? 1 : 0; + } + } + } - /** - * used by label in interface to show the active click action - * @return String representation of click action - */ - public String clickActionName() { - // TODO : initially return "sheep" or "cell" - // depending on clickActionFlag - return ""; - } + grid = newGrid; // Update the grid with new state + } + private int countAliveNeighbors(int x, int y) { + int count = 0; + for (int dy = -1; dy <= 1; dy++) { + for (int dx = -1; dx <= 1; dx++) { + if (dx == 0 && dy == 0) continue; + int nx = x + dx; + int ny = y + dy; + if (nx >= 0 && nx < COL_NUM && ny >= 0 && ny < LINE_NUM) { + count += grid[ny][nx]; + } else if (loopingBorder) { + nx = (nx + COL_NUM) % COL_NUM; + ny = (ny + LINE_NUM) % LINE_NUM; + count += grid[ny][nx]; + } + } + } + return count; + } + + public void stopSimu() { + stopFlag = true; + } + + public void togglePause() { + pauseFlag = !pauseFlag; + } + + public void clickCell(int x, int y) { + grid[y][x] = (grid[y][x] == 1) ? 0 : 1; + } + + public int getCell(int x, int y) { + return grid[y][x]; + } + + public ArrayList getAnimals() { + return agents; + } + + public ArrayList getNeighboringAnimals(int x, int y, int radius) { + ArrayList inArea = new ArrayList(); + for (int i = 0; i < agents.size(); i++) { + Agent agent = agents.get(i); + if (agent.isInArea(x, y, radius)) { + inArea.add(agent); + } + } + return inArea; + } + + public void setCell(int x, int y, int val) { + grid[y][x] = val; + } + + public ArrayList getSaveState() { + ArrayList state = new ArrayList(); + for (int y = 0; y < LINE_NUM; y++) { + StringBuilder line = new StringBuilder(); + for (int x = 0; x < COL_NUM; x++) { + line.append(grid[y][x]).append(";"); + } + state.add(line.toString()); + } + return state; + } + + public void generateRandom(float chanceOfLife) { + Random rand = new Random(); + for (int y = 0; y < LINE_NUM; y++) { + for (int x = 0; x < COL_NUM; x++) { + grid[y][x] = rand.nextFloat() < chanceOfLife ? 1 : 0; + } + } + } + + public boolean isLoopingBorder() { + return loopingBorder; + } + + public void toggleLoopingBorder() { + loopingBorder = !loopingBorder; + } + + public void setLoopDelay(int delay) { + loopDelay = delay; + } + + public void toggleClickAction() { + clickActionFlag = !clickActionFlag; + } + + public ArrayList getRule() { + ArrayList rule = new ArrayList(); + StringBuilder surviveLine = new StringBuilder(); + for (int value : fieldSurviveValues) { + surviveLine.append(value).append(";"); + } + rule.add(surviveLine.toString()); + + StringBuilder birthLine = new StringBuilder(); + for (int value : fieldBirthValues) { + birthLine.append(value).append(";"); + } + rule.add(birthLine.toString()); + return rule; + } + + public void loadRule(ArrayList lines) { + if (lines.size() <= 0) { + System.out.println("empty rule file"); + return; + } + + fieldSurviveValues.clear(); + fieldBirthValues.clear(); + + String surviveLine = lines.get(0); + String birthLine = lines.get(1); + String[] surviveElements = surviveLine.split(";"); + for (String elem : surviveElements) { + int value = Integer.parseInt(elem); + fieldSurviveValues.add(value); + } + String[] birthElements = birthLine.split(";"); + for (String elem : birthElements) { + int value = Integer.parseInt(elem); + fieldBirthValues.add(value); + } + } + + public ArrayList getAgentsSave() { + ArrayList agentState = new ArrayList(); + for (Agent agent : agents) { + String state = agent.getX() + ";" + agent.getY() + ";" + agent.getDisplayColor().getRGB(); + agentState.add(state); + } + return agentState; + } + + public void loadAgents(ArrayList stringArray) { + agents.clear(); + for (String line : stringArray) { + String[] elements = line.split(";"); + int x = Integer.parseInt(elements[0]); + int y = Integer.parseInt(elements[1]); + Color color = new Color(Integer.parseInt(elements[2])); + agents.add(new BasicAgent(x, y, color)); + } + } + + public void loadSaveState(ArrayList stringArray) { + for (int y = 0; y < LINE_NUM; y++) { + String[] line = stringArray.get(y).split(";"); + for (int x = 0; x < COL_NUM; x++) { + grid[y][x] = Integer.parseInt(line[x]); + } + } + } + + public String clickActionName() { + return clickActionFlag ? "cell" : "sheep"; + } }