package backend; import java.io.BufferedReader; import java.io.FileReader; import java.util.ArrayList; 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 int clickActionFlag; private int loopDelay = 150; private int[][] world; private int [] survivalRulesArray; private int[] birthRulesArray; //TODO : add missing attribute(s) private int stepCount; public Simulator(MyInterface mjfParam) { mjf = mjfParam; //stopFlag=false; //not necessary since i set the state when pressing the button start pauseFlag=false; loopingBorder=false; clickActionFlag=0; agents = new ArrayList(); fieldBirthValues = new ArrayList(); fieldSurviveValues = new ArrayList(); world =new int[getWidth()][getHeight()]; //TODO : add missing attribute initialization //Default rule : Survive always, birth never for(int i =0; i<9; i++) { fieldSurviveValues.add(i); } } public int getWidth() { return COL_NUM; } public int getHeight() { return LINE_NUM; } public int[] getBirthRulesArray() { return this.birthRulesArray; } public int[] getSurvivalRulesArray() { return this.survivalRulesArray; } public void loadRule(ArrayList row) { String surviveRulesRow = row.get(0); String[] surviveCells = surviveRulesRow.split(";"); String birthRulesRow = row.get(1); String[] birthCells = birthRulesRow.split(";"); survivalRulesArray = new int[surviveCells.length]; birthRulesArray = new int[birthCells.length]; if (row.size() <= 0) { System.out.println("wrong file buddy, this one's empty"); }else if(surviveCells.length<=0) { System.out.println("wrong file buddy, this one's does not have survival rules, won't work"); return; }else { for (int x = 0; x < birthCells.length; x++) { String elem = birthCells[x]; int value = Integer.parseInt(elem); birthRulesArray[x] = value; } //determines the number of alive neighboring cells needed to birth, and places them in the birthCells list for (int x = 0; x < surviveCells.length; x++) { String elem = surviveCells[x]; int value = Integer.parseInt(elem); survivalRulesArray[x] = value; } //determines the number of alive neighboring cells needed to survive, and places them in the surviveCells list } } //Should probably stay as is public void run() { 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(); } } } } /** * method called at each step of the simulation * makes all the actions to go from one step to the other */ private int countAliveNeighbors(int x, int y) { int count = 0; int[][] directions = {{-1, -1}, {-1, 0}, {-1, 1}, {0, -1}, {0, 1}, {1, -1}, {1, 0}, {1, 1}}; for (int[] dir : directions) { int nx = x + dir[0]; int ny = y + dir[1]; if (nx >= 0 && nx < getWidth() && ny >= 0 && ny < getHeight()) { count += world[nx][ny]; } else if (loopingBorder) { nx = (nx + getWidth()) % getWidth(); ny = (ny + getHeight()) % getHeight(); count += world[nx][ny]; } } return count; } /*public void setWorld(int[][] world, int width, int height) { this.world = world; this.COL_NUM = width; this.LINE_NUM = height; }*/ public void makeStep() { // agent behaviors first // only modify if sure of what you do // to modify agent behavior, see liveTurn method // in agent classes ArrayList newAgents = new ArrayList<>(); for(Agent agent : agents) { ArrayList neighbors = this.getNeighboringAnimals( agent.getX(), agent.getY(), ANIMAL_AREA_RADIUS);} /*if(!agent.liveTurn( neighbors, this)) { agents.remove(agent); {*/ // Apply Game of Life rules /*for (int x = 0; x < getWidth(); x++) { for (int y = 0; y < getHeight(); y++) { int aliveNeighbors = countAliveNeighbors(x, y); if (world[x][y] == 1) { newWorld[x][y] = (aliveNeighbors < 2 || aliveNeighbors > 3) ? 0 : 1; } else { newWorld[x][y] = (aliveNeighbors == 3) ? 1 : 0; } } }*/ int[][] newWorld = new int[getWidth()][getHeight()]; for (int x = 0; x < getWidth(); x++) { for (int y = 0; y < getHeight(); y++) { int aliveNeighbors = countAliveNeighbors(x, y); if (getCell(x, y) == 1) { //newWorld[x][y] = shouldSurvive(aliveNeighbors) ? 1 : 0; } else { //newWorld[x][y] = shouldBeBorn(aliveNeighbors) ? 1 : 0; } } } //setWorld(newWorld, getWidth(), getHeight()); } //world = newWorld; //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; } public void startSimu() { // same as stopSimu but will set the stopFlag value as false. stopFlag=false; } /* * method called when clicking pause button */ public void togglePause() { pauseFlag= ! pauseFlag; // It changes the boolean value associated to pauseFlag (pauseFlag= not pauseFlag) } /** * method called when clicking on a cell in the interface */ public void clickCell(int x, int y) { if (clickActionFlag==0) { // cell setCell(x, y, getCell(x, y) == 1 ? 0 : 1); } else if (clickActionFlag==1) { // sheep ArrayList nearby=getNeighboringAnimals(x,y,1); //look if the cell has an agent if (nearby.isEmpty()) { Sheep sheep = new Sheep(x,y); agents.add(sheep); }else { for (Agent animal:nearby) { agents.remove(animal); } } } else if (clickActionFlag==2) { // wolf ArrayList nearby=getNeighboringAnimals(x,y,1); if (nearby.isEmpty()) { Sheep sheep = new Sheep(x,y); agents.add(sheep); }else { for (Agent animal:nearby) { agents.remove(animal); } } } } /** * 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) { return world[x][y]; //get the value (dead or alive) of my cell at x y } /** * * @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() { ArrayList saveState = new ArrayList<>(); for (int j = 0; j < getHeight(); j++) { StringBuilder lineState = new StringBuilder(); for (int i = 0 ; i < getWidth() ; i++) { lineState.append(getCell(i, j)); if (j < getHeight() -1) { lineState.append(";"); } } saveState.add(lineState.toString()); } return saveState; } /** * * @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() { //to check ArrayList rule = new ArrayList<>(); for (int i = 0; i < getHeight(); i++) { StringBuilder lineState = new StringBuilder(); for (int j = 0 ; j < getHeight() ; j++) { // je crois qu'il y a un probleme, il fau+t mettre getWidth je crois lineState.append(getCell(i, j)); if (j < getWidth() - 1) { lineState.append(";"); } } rule.add(lineState.toString()); } return rule; } public void generateRandom(float chanceOfLife) { /* * 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 */ Random rand = new Random(); for (int x = 0; x < COL_NUM; x++) { for (int y = 0; y < LINE_NUM; y++) { world[x][y] = rand.nextFloat() < chanceOfLife ? 1 : 0; } } } public boolean isLoopingBorder() { return loopingBorder; } public void toggleLoopingBorder() { if (loopingBorder){ loopingBorder = false; }else { loopingBorder = true; } } public void setLoopDelay(int delay) { loopDelay = delay; } public void toggleClickAction() { if (clickActionFlag < 2) { clickActionFlag ++ ; }else if (clickActionFlag == 2) { clickActionFlag=0; } } /** * prepare the content of a file saving present ruleSet * as you might want to save a state, * initialy written in this class constructor * as a file for future use * @return File content as an ArrayList of Lines (String) * @see loadRule for inverse process */ public ArrayList getAgentsSave() { ArrayList agentsList = new ArrayList(); String sheepLine = "1,"; String wolfLine = "2,"; for (Agent agent : agents) { int x = agent.getX(); int y = agent.getY(); if (agent instanceof Sheep) { sheepLine = sheepLine + x + ";" + y + ","; } else if (agent instanceof Wolf) { wolfLine = wolfLine + x + ";" + y + ","; } } agentsList.add(sheepLine); agentsList.add(wolfLine); return agentsList; } public void loadAgents(ArrayList stringArray) { //TODO : Same idea as other load methods, but for agent list for(int y =0; y