package backend; import java.awt.Color; import java.io.BufferedReader; import java.io.FileReader; import java.util.ArrayList; import java.util.Random; import windowInterface.MyInterface; import windowInterface.JPanelDraw; public class Simulator extends Thread { private MyInterface mjf; public Rules rules; //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[][] worldGrid; private World world; //get the World instance //TODO : add missing attribute(s) private int stepCount; public Simulator(MyInterface mjfParam, int worldWidth, int worldHeight) { 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 World(worldWidth, worldHeight); //to initialize the world instance worldGrid = new int[world.getWidth()][world.getHeight()]; this.rules = new Rules(); //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 world.getWidth(); } public int getHeight() { return world.getHeight(); } public World getActualWorld(){ return world; } public int getCell(int x, int y) { return world.getCell(x, y); } public void setWorld(World world) { this.world = world; } //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 += worldGrid[nx][ny]; } else if (loopingBorder) { nx = (nx + getWidth()) % getWidth(); ny = (ny + getHeight()) % getHeight(); count += worldGrid[nx][ny]; } } return count; } public void makeStep() { // agent behaviors first // only modify if sure of what you do // to modify agent behavior, see liveTurn method // in agent classes int[][] newWorld = new int[getWidth()][getHeight()]; /*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; // } // } //} //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 world.setCell(x, y, getCell(x, y) == 1 ? 0 : 1); } else if (clickActionFlag==1) { // sheep 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); } } } else if (clickActionFlag==2) { // wolf ArrayList nearby=getNeighboringAnimals(x,y,1); if (nearby.isEmpty()) { Wolf wolf = new Wolf(x,y); agents.add(wolf); }else { for (Agent animal:nearby) { agents.remove(animal); } } } } /** * * @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; } /* * Advice for the generateRandom : * as you should probably have a separate class * representing the field of cells... * maybe just make a constructor in there * and use it here */ 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; } } public ArrayList getAgentsSave() { //TODO : Same idea as the other save method, but for agents return null; } public void loadAgents(ArrayList stringArray) { //TODO : Same idea as other load methods, but for agent list } /** * 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 if (clickActionFlag==0) { return "cell"; }else if (clickActionFlag==1) { return "sheep"; }else if (clickActionFlag==2) { return "wolf"; }else { return "error"; } } public void reset() { for (int i = 0; i < getHeight(); i++) { for (int j = 0 ; j < getWidth() ; j++) { world.setCell(i,j,0); } } this.stepCount = 0; } public boolean isWorldEmpty() { for (int x = 0; x < getWidth(); x++) { for (int y = 0; y < getHeight(); y++) { if (getCell(x, y) != 0) { //0 represents a "dead" cell return false; } } } return true; } public void startSimulation() { if (isWorldEmpty()) { mjf.clicLoadFileButtonCSV("World/baseworld.csv"); } } public void generateRandomWorld(float chanceOfLife, JPanelDraw panelDraw) { Random rand = new Random(); for (int x = 0; x < world.getWidth(); x++) { for (int y = 0; y < world.getHeight(); y++) { float randomValue = rand.nextFloat(); world.setCell(x, y, randomValue < chanceOfLife ? 1 : 0); } } panelDraw.repaint(); } }